Esempio n. 1
0
class Rcon(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.mcr = MCRcon(os.environ["rcon_ip"], os.environ["rcon_pass"],
                          int(os.environ["rcon_port"]))
        self.mcr.connect()

    async def send_resp(self, resp: str, ctx: commands.Context):
        if str(resp) == "":
            await ctx.channel.send(
                "There was no response text for this command execution.")
            return
        await ctx.channel.send(resp)

    @commands.group(name="rcon")
    async def rcon(self, ctx):
        self.mcr.command(f"//;{ctx.message.content}")

    @rcon.command(name="exec")
    @commands.has_any_role("Moderator", "Administrator")
    async def rcon_exec(self, ctx, *command):
        command = " ".join(command)
        resp = self.mcr.command(command)
        await self.send_resp(resp, ctx)

    @rcon.command(name="whitelist")
    @commands.has_any_role("Big Brain", "Moderator", "Administrator",
                           "Developer")
    async def rcon_whitelist(self, ctx, ign, typ="allow"):
        if typ == "allow":
            resp = self.mcr.command(f"whitelist add {ign}")
            await self.send_resp(resp, ctx)
        else:
            resp = self.mcr.command(f"whitelist remove {ign}")
            await self.send_resp(resp, ctx)
Esempio n. 2
0
def send_commands(server_ip, rcon_password, commands, buyer, rcon_port):
    server_ip = str(server_ip).split(':')[0]
    mcr = MCRcon(server_ip, rcon_password, int(rcon_port))
    mcr.connect()
    for command in commands:
        mcr.command(command.replace("{PLAYER}", buyer))
    mcr.disconnect()
Esempio n. 3
0
    def run_server(self) -> ContextManager[MCRcon]:
        server_process = Popen(
            ["java", "-jar", self.server_path.name, "--nogui"],
            cwd=self.server_dir)
        # There must be a better way
        rcon = None
        for i in range(72):
            sleep(5)
            try:
                rcon = MCRcon("localhost", self.RCON_PASSWORD,
                              int(self.RCON_PORT))

                # tries to create a connection
                with rcon:
                    pass
                break
            except ConnectionError:
                continue
        else:
            server_process.kill()
            pytest.fail(
                "Could not connect to the minecraft server after 5 minutes")

        with rcon:
            try:
                yield rcon
            finally:
                rcon.command("stop")
                server_process.wait(30)
Esempio n. 4
0
class RconInterface:
    def __init__(self, ip_address, port, password, name="None"):
        self.con = MCRcon(ip_address, password, port=port)
        self.name = name
        try:
            self.con.connect()
        except ConnectionRefusedError:
            print(f'rcon failed to connect {self.con.host}:{self.con.port}')

    def getstatus(self):
        return str(self.con.socket).find("0.0.0.0") == -1

    def reconnect(self):
        if not self.getstatus():
            self.con.connect()

    def command(self, command, retrys=0):
        try:
            return self.con.command(command)
        except (ConnectionResetError, ConnectionRefusedError):
            self.con.connect()
            if retrys < 5:
                self.command(command, retrys + 1)

    def __del__(self):
        self.con.disconnect()
Esempio n. 5
0
def get_stats():
    mcr = MCRcon("0.0.0.0", "factory")
    mcr.connect()
    resp = mcr.command(write_stats_command)
    #do something with the response
    mcr.disconnect()
    return resp
Esempio n. 6
0
class Connection:
    def __init__(self, address, port, secret):
        self.mcr = MCRcon(address, secret)
        self.mcr.connect()

    def send(self, msg):
        resp = self.mcr.command(msg)
        return resp
Esempio n. 7
0
def run_code(rcon: MCRcon, test_world: MCWorld,
             code: str) -> Tuple[Optional[int], str]:
    config = Config()
    config.output_dir = Path(test_world.getDatapackPath()) / "mcscript"
    config.input_string = TEST_TEMPLATE.format(code)
    datapack = compileMcScript(config)
    Logger.info(datapack.getMainDirectory().getPath("functions").files)
    generate_datapack(config, datapack)

    rcon.command("reload")
    result = rcon.command(f"scoreboard players get result mcscript_test")

    match = RESULT_PATTERN.match(result)
    if match is not None:
        value = int(match.group(1))
    else:
        value = None

    return value, result
Esempio n. 8
0
async def whitelist_add_user(user_id, username):
    rcon: tuple = await get_rcon()
    mcr = MCRcon(host=rcon[0], port=int(rcon[1]), password=rcon[2])
    mcr.connect()

    if await has_whitelist(user_id):
        rcon_response = mcr.command(
            f'whitelist remove {await get_mc_username(user_id)}')
        print(rcon_response)
        db.execute('UPDATE minecraft_users SET mc_username=? WHERE user_id=?',
                   (username, user_id))
    else:
        db.execute('INSERT INTO minecraft_users VALUES(?,?)',
                   (user_id, username))
    connection.commit()

    rcon_response = mcr.command(f'whitelist add {username}')
    print(rcon_response)
    mcr.disconnect()
Esempio n. 9
0
def main():
    print("Factocli RCON Client Started")
    print("Connecting to RCON Address")
    mcr = MCRcon("0.0.0.0", "factory")
    mcr.connect()
    resp = mcr.command(write_stats_command)
    print("Sending command...")
    print("Response: "+resp)
    print("Disconnecting...")
    mcr.disconnect()
    print("Done")
Esempio n. 10
0
async def whitelist_remove_user(user_id):
    rcon: tuple = await get_rcon()
    mcr = MCRcon(host=rcon[0], port=int(rcon[1]), password=rcon[2])

    mcr.connect()
    rcon_response = mcr.command(
        f'whitelist remove {await get_mc_username(user_id)}')
    print(rcon_response)
    mcr.disconnect()

    db.execute('DELETE FROM minecraft_users WHERE user_id=?', (user_id, ))
    connection.commit()
Esempio n. 11
0
def check_shutoff(config):
    sum_players = 0

    server_store = open(config['server_store'], 'r')
    cached_statuses = json.loads(server_store.read())
    server_store.close()

    for key, val in cached_statuses.items():
        sum_players += sum(val)

    if config['debug']:
        print('========== Check shutoff ==========')
        pprint({'total_historical_num_players': sum_players})
        print()

    all_long_running = len(
        list(
            filter(
                lambda count: count != 3,
                list(
                    map(lambda server: len(cached_statuses[server]),
                        cached_statuses.keys()))))) == 0

    if sum_players == 0 and all_long_running:
        if config['debug']:
            print('========= Saving servers ==========')
        for server in cached_statuses.keys():
            try:
                host = config['servers'][server]['host']
                port = config['servers'][server]['port']
                password = config['servers'][server]['password']

                mcr = MCRcon(host=host, password=password, port=port)
                mcr.connect()

                resp = mcr.command('save-all')

                if config['debug']:
                    print(f'save-all response for {server}: {resp}')
                logging.info(f'save-all response for {server}: {resp}')

                mcr.disconnect()
            except Exception as e:
                logging.warning(
                    f'An error occured for server {server}\n{str(e)}')

        if config['debug']:
            print('===== Shutting down instance ======')
        else:
            with open(config['server_store'], 'w+') as server_store:
                server_store.write(json.dumps({}, indent=2))
            EC2 = boto3.client('ec2', region_name=config['region_name'])
            resp = EC2.stop_instances(InstanceIds=[config['instance_id']])
Esempio n. 12
0
class _RConsole:
    __lock_connection_action = Lock()
    __lock_auto_close_thread = Lock()

    __disconnect_seconds = 100

    def __init__(self, host, port: int, password, use_tls: bool = True):
        tls = {
            True: 1,
            False: 0
        }
        self.__auto_close_timer = AsyncCountdownTimer(self.__disconnect_seconds, self.__disconnect)
        self.__con = MCRcon(host, password, port, tls[use_tls])

    def execute(self, command: str, timeout: int = 0) -> str:
        """
        Execute a command, return its response. :param command: the command to be executed. :param timeout: timeout
        in seconds. If it is set to 0, the function waits until a response is received. If timed out,
        an `TimedOutException` will be thrown. :return: the response echo.
        """
        # check connection
        with self.__lock_connection_action:
            if not self.__con.socket:
                self.__con.connect()
        with self.__lock_auto_close_thread:
            self.__auto_close_timer.reset()
            self.__auto_close_timer = AsyncCountdownTimer(self.__disconnect_seconds, self.__disconnect)
            self.__auto_close_timer.start()

        # TODO: implement timeout
        if timeout:
            raise NotImplementedError("Sorry, timeout has not been implemented")

        # execute command
        logging.info(f'Execute command: {command}')
        return self.__con.command(command)

    def __del__(self):
        if self.__auto_close_timer:
            self.__auto_close_timer.reset()

    def __disconnect(self):
        disconnected = False
        with self.__lock_connection_action:
            if self.__con.socket:
                self.__con.disconnect()
                disconnected = True
        if disconnected:
            logging.info('Console is inactive. Disconnected from RCON server.')
Esempio n. 13
0
class Scoreboard(object):
    def __init__(self, host, password, port=25575):
        self.rcon = MCRcon(host, password, port=port)

    def __enter__(self):
        self.rcon.connect()

        return self

    def __exit__(self, type, value, tb):
        self.rcon.disconnect()

    def get_value_for_player(self, nickname: str, score: str) -> int:
        cmd_result = self.rcon.command('scoreboard players get {} {}'.format(
            nickname, score))
        value = cmd_result[cmd_result.find('has') + 4:cmd_result.find('[') - 1]
        return int(value)
Esempio n. 14
0
 async def whitelist(ctx):
     if rconIp == "":
         await ctx.author.send(
             "please run $setup before using any commmands")
         return False
     if ctx.channel.id == CHANNELID or CHANNELID == 0:
         if poll(ctx.author.id) == False:
             first_set = await query(ctx)
             #print("done with first set: " + str(first_set[0]) + str(first_set[1]) + str(first_set[2]) + str(first_set[3]))
             if first_set != False:
                 second_set = await newsletterQuery(ctx)
                 #print("done with second set: " + str(second_set))
                 if second_set != False:
                     conn = psycopg2.connect(database=pgdb,
                                             user=pgUser,
                                             password=pgPass,
                                             host=pgHost,
                                             port=pgPort)
                     cur = conn.cursor()
                     try:
                         #print("------------showing insert to database---------\nuser_id:"+str(ctx.author.id)+"\nfirst_name:"+str(first_set[0])+"\nlast_name:"+str(first_set[1])+"\nuuid:"+str(first_set[2])+"\nusername:"******"\nemail:"+str(second_set))
                         cur.execute(
                             'INSERT INTO whitelist ("user_id", "first_name", "last_name", "uuid", "username", "email", "isBanned") VALUES ('
                             + str(ctx.author.id) + ', \'' +
                             str(first_set[0]) + '\', \'' +
                             str(first_set[1]) + '\', N\'' +
                             str(first_set[2]) + '\', N\'' +
                             str(first_set[3]) + '\', N\'' +
                             str(second_set) + '\', ' + str(0) + ');')
                         conn.commit()
                     except Exception as e:
                         print("DB error: \n" + str(e))
                     mcr = MCRcon(str(rconIp),
                                  str(rconPass),
                                  port=int(rconPort))
                     mcr.connect()
                     resp = mcr.command("whitelist add " +
                                        str(first_set[3]))
                     print(resp)
                     mcr.disconnect()
                     await ctx.author.send(embed=addFinish_embed)
                     conn.close()
         else:
             if poll(ctx.author.id)[6] == 0:
                 await edit(ctx)
Esempio n. 15
0
    async def remove(ctx):
        if rconIp == "":
            await ctx.author.send(
                "please run $setup before using any commmands")
            return False
        if ctx.channel.id == CHANNELID or CHANNELID == 0:
            if poll(ctx.author.id) != False:
                if poll(ctx.author.id)[6] == 0:
                    username = ctx.author
                    msgPrompt = await username.send(
                        embed=removeConfirmPrompt_embed)
                    thumbsup, thumbsdown = '👍', '👎'
                    await msgPrompt.add_reaction(thumbsup)
                    await msgPrompt.add_reaction(thumbsdown)

                    def checkreact(reaction, react):
                        react = str(reaction.emoji)
                        return ((react == '👍' or react == '👎')
                                and (reaction.message.id == msgPrompt.id))

                    await asyncio.sleep(.1)
                    try:
                        confirmation = await bot.wait_for('reaction_add',
                                                          check=checkreact,
                                                          timeout=300)
                    except TimeoutError:
                        print(
                            "User " + str(ctx.author) +
                            "Timed out on removal confirmation, User remains in database"
                        )
                        await ctx.author.send(embed=timeout_embed)
                        return False
                    else:
                        if str(confirmation[0].emoji) == '👍':
                            mcUser = poll(username.id)[4]
                            mcr = MCRcon(str(rconIp),
                                         str(rconPass),
                                         port=int(rconPort))
                            mcr.connect()
                            resp = mcr.command("whitelist remove " + mcUser)
                            print(resp)
                            mcr.disconnect()
                            remove_player(username.id)
                            await username.send(embed=removeConfirm_embed)
Esempio n. 16
0
 def rcon_thread(self):
     while self.running:
         try:
             s = socket()
             s.settimeout(1)
             s.connect(('localhost', 25575))
             s.close()
             rcon = MCRcon('localhost', PASSWORD)
             rcon.connect()
             self.ready = True
             while self.running:
                 time.sleep(2)
                 self.players = [
                     name for name in rcon.command("list").split(':')
                     [1].split(', ') if name
                 ]
         except Exception as e:
             self.ready = False
             self.players = []
             time.sleep(0.2)
     print("rcon thread stopped")
Esempio n. 17
0
def Shutdown():
    if not request.headers.get("User-Agent") == USER_AGENT:
        error_message = {"state": 0, "error": "Invalid User-Agent."}
        return make_response(jsonify(error_message), 400)
    if not request.headers.get("Content-Type") == CONTENT_TYPE:
        error_message = {"state": 0, "error": "Invalid Content-Type."}
        return make_response(jsonify(error_message), 400)
    try:
        mcr = MCRcon(IPADDRESS, PASSWORD, RCONPORT)
        mcr.connect()
        resp = mcr.command("saveworld")
        mcr.command(
            "Broadcast The world has been saved. The server will be shutdown in 1 minute."
        )
        time.sleep(60)
        resp = mcr.command("saveworld")
        mcr.command("Broadcast Stop the server.")
        mcr.command("DoExit")
        print(resp)
        mcr.disconnect()
    except:
        pass
    session = winrm.Session(IPADDRESS, auth=(USER, PASSWORD))
    try:
        session.run_ps("shutdown -s -f -t 120")
        success_message = {
            "state": 1,
            "body": "The request was executed successfully."
        }
    except:
        success_message = {
            "state":
            0,
            "body":
            "The request was processed successfully, but the shutdown process was not executed properly.",
        }
    return make_response(jsonify(success_message), 200)
Esempio n. 18
0
def sendRconCommands(cmds, delay = 0.03):
    mcr = MCRcon("127.0.0.1", _rconPass, _rconPort, 0)

    try:
        mcr.connect()
    except Exception as ex:
        logmsg(f'ERROR: Unable to connect to rcon server: {ex}')
        return

    # flatten all commands
    cmds = [item for sublist in cmds for item in sublist]

    for cmd in cmds:
        try:
            resp = mcr.command(cmd)
            logmsg(f'RCON Command: {cmd}: {resp}')
            time.sleep(delay)
        except Exception as ex:
            logmsg(f'ERROR: Unable to send rcon command: {cmd}')
            logmsg(f'       {ex}')
            break

    mcr.disconnect()
Esempio n. 19
0
    async def on_ready():
        print("starting bot and setting config")
        #load_dotenv()
        global prefix
        global CHANNELID
        global rconIp
        global rconPort
        global rconPass
        conn = psycopg2.connect(database=pgdb,
                                user=pgUser,
                                password=pgPass,
                                host=pgHost,
                                port=pgPort)
        cur = conn.cursor()
        cur.execute('SELECT * FROM serverconfig;')
        a = cur.fetchone()
        conn.close()
        if a != None:
            print('showing server stuff: \nserver: ' + str(a[0]) +
                  '\nchannel_id:' + str(a[1]) + '\nprefix:' + str(a[2]) +
                  '\nip:' + str(a[3]) + '\nport:' + str(a[4]) + '\npass:' +
                  str(a[5]))
        if a != None:
            bot.command_prefix = str(a[2])
            CHANNELID = int(a[1])
            rconIp = str(a[3])
            rconPort = int(a[4])
            rconPass = str(a[5])
            mcr = MCRcon(str(rconIp), str(rconPass))
            mcr.connect()
            resp = mcr.command("/help")
            print(resp)
            mcr.disconnect()
        else:
            print("please run $setup")

        print("Bot ready")
class MinecraftCollector(object):
    def __init__(self):
        self.statsdirectory = "/world/stats"
        self.playerdirectory = "/world/playerdata"
        self.advancementsdirectory = "/world/advancements"
        self.betterquesting = "/world/betterquesting"
        self.map = dict()
        self.questsEnabled = False
        self.rcon = None
        if os.path.isdir(self.betterquesting):
            self.questsEnabled = True
        schedule.every().day.at("01:00").do(self.flush_playernamecache)

    def get_players(self):
        return [
            f[:-5] for f in listdir(self.statsdirectory)
            if isfile(join(self.statsdirectory, f))
        ]

    def flush_playernamecache(self):
        print("flushing playername cache")
        self.map = dict()
        return

    def uuid_to_player(self, uuid):
        uuid = uuid.replace('-', '')
        if uuid in self.map:
            return self.map[uuid]
        else:
            result = requests.get('https://api.mojang.com/user/profiles/' +
                                  uuid + '/names')
            self.map[uuid] = result.json()[-1]['name']
            return (result.json()[-1]['name'])

    def rcon_command(self, command):
        if self.rcon == None:
            self.rcon = MCRcon(os.environ['RCON_HOST'],
                               os.environ['RCON_PASSWORD'],
                               port=int(os.environ['RCON_PORT']))
            self.rcon.connect()
        try:
            response = self.rcon.command(command)
        except BrokenPipeError:
            print("Lost RCON Connection, trying to reconnect")
            self.rcon.connect()
            response = self.rcon.command(command)

        return response

    def get_server_stats(self):
        metrics = []
        if not all(x in os.environ for x in ['RCON_HOST', 'RCON_PASSWORD']):
            return []
        dim_tps = Metric('dim_tps', 'TPS of a dimension', "counter")
        dim_ticktime = Metric('dim_ticktime',
                              "Time a Tick took in a Dimension", "counter")
        overall_tps = Metric('overall_tps', 'overall TPS', "counter")
        overall_ticktime = Metric('overall_ticktime', "overall Ticktime",
                                  "counter")
        player_online = Metric('player_online', "is 1 if player is online",
                               "counter")
        entities = Metric('entities', "type and count of active entites",
                          "counter")

        metrics.extend([
            dim_tps, dim_ticktime, overall_tps, overall_ticktime,
            player_online, entities
        ])

        if 'FORGE_SERVER' in os.environ and os.environ[
                'FORGE_SERVER'] == "True":
            # dimensions
            resp = self.rcon_command("forge tps")
            dimtpsregex = re.compile(
                "Dim\s*(-*\d*)\s\((.*?)\)\s:\sMean tick time:\s(.*?) ms\. Mean TPS: (\d*\.\d*)"
            )
            for dimid, dimname, meanticktime, meantps in dimtpsregex.findall(
                    resp):
                dim_tps.add_sample('dim_tps',
                                   value=meantps,
                                   labels={
                                       'dimension_id': dimid,
                                       'dimension_name': dimname
                                   })
                dim_ticktime.add_sample('dim_ticktime',
                                        value=meanticktime,
                                        labels={
                                            'dimension_id': dimid,
                                            'dimension_name': dimname
                                        })
            overallregex = re.compile(
                "Overall\s?: Mean tick time: (.*) ms. Mean TPS: (.*)")
            overall_tps.add_sample('overall_tps',
                                   value=overallregex.findall(resp)[0][1],
                                   labels={})
            overall_ticktime.add_sample('overall_ticktime',
                                        value=overallregex.findall(resp)[0][0],
                                        labels={})

            # entites
            resp = self.rcon_command("forge entity list")
            entityregex = re.compile("(\d+): (.*?:.*?)\s")
            for entitycount, entityname in entityregex.findall(resp):
                entities.add_sample('entities',
                                    value=entitycount,
                                    labels={'entity': entityname})

        # dynmap
        if 'DYNMAP_ENABLED' in os.environ and os.environ[
                'DYNMAP_ENABLED'] == "True":
            dynmap_tile_render_statistics = Metric(
                'dynmap_tile_render_statistics',
                'Tile Render Statistics reported by Dynmap', "counter")
            dynmap_chunk_loading_statistics_count = Metric(
                'dynmap_chunk_loading_statistics_count',
                'Chunk Loading Statistics reported by Dynmap', "counter")
            dynmap_chunk_loading_statistics_duration = Metric(
                'dynmap_chunk_loading_statistics_duration',
                'Chunk Loading Statistics reported by Dynmap', "counter")
            metrics.extend([
                dynmap_tile_render_statistics,
                dynmap_chunk_loading_statistics_count,
                dynmap_chunk_loading_statistics_duration
            ])

            resp = self.rcon_command("dynmap stats")

            dynmaptilerenderregex = re.compile(
                "  (.*?): processed=(\d*), rendered=(\d*), updated=(\d*)")
            for dim, processed, rendered, updated in dynmaptilerenderregex.findall(
                    resp):
                dynmap_tile_render_statistics.add_sample(
                    'dynmap_tile_render_statistics',
                    value=processed,
                    labels={
                        'type': 'processed',
                        'file': dim
                    })
                dynmap_tile_render_statistics.add_sample(
                    'dynmap_tile_render_statistics',
                    value=rendered,
                    labels={
                        'type': 'rendered',
                        'file': dim
                    })
                dynmap_tile_render_statistics.add_sample(
                    'dynmap_tile_render_statistics',
                    value=updated,
                    labels={
                        'type': 'updated',
                        'file': dim
                    })

            dynmapchunkloadingregex = re.compile(
                "Chunks processed: (.*?): count=(\d*), (\d*.\d*)")
            for state, count, duration_per_chunk in dynmapchunkloadingregex.findall(
                    resp):
                dynmap_chunk_loading_statistics_count.add_sample(
                    'dynmap_chunk_loading_statistics',
                    value=count,
                    labels={'type': state})
                dynmap_chunk_loading_statistics_duration.add_sample(
                    'dynmap_chunk_loading_duration',
                    value=duration_per_chunk,
                    labels={'type': state})

        # player
        resp = self.rcon_command("list")
        playerregex = re.compile("players online:(.*)")
        if playerregex.findall(resp):
            for player in playerregex.findall(resp)[0].split(","):
                if not player.isspace():
                    player_online.add_sample(
                        'player_online',
                        value=1,
                        labels={'player': player.lstrip()})

        return metrics

    def get_player_quests_finished(self, uuid):
        with open(self.betterquesting + "/QuestProgress.json") as json_file:
            data = json.load(json_file)
            json_file.close()
        counter = 0
        for _, value in data['questProgress:9'].items():
            for _, u in value['tasks:9']['0:10']['completeUsers:9'].items():
                if u == uuid:
                    counter += 1
        return counter

    def get_player_stats(self, uuid):
        with open(self.statsdirectory + "/" + uuid + ".json") as json_file:
            data = json.load(json_file)
            json_file.close()
        nbtfile = nbt.nbt.NBTFile(self.playerdirectory + "/" + uuid + ".dat",
                                  'rb')
        data["stat.XpTotal"] = nbtfile.get("XpTotal").value
        data["stat.XpLevel"] = nbtfile.get("XpLevel").value
        data["stat.Score"] = nbtfile.get("Score").value
        data["stat.Health"] = nbtfile.get("Health").value
        data["stat.foodLevel"] = nbtfile.get("foodLevel").value
        with open(self.advancementsdirectory + "/" + uuid +
                  ".json") as json_file:
            count = 0
            advancements = json.load(json_file)
            for key, value in advancements.items():
                if key in ("DataVersion"):
                    continue
                if value["done"] == True:
                    count += 1
        data["stat.advancements"] = count
        if self.questsEnabled:
            data["stat.questsFinished"] = self.get_player_quests_finished(uuid)
        return data

    def update_metrics_for_player(self, uuid):
        data = self.get_player_stats(uuid)
        name = self.uuid_to_player(uuid)
        blocks_mined = Metric('blocks_mined', 'Blocks a Player mined',
                              "counter")
        blocks_picked_up = Metric('blocks_picked_up',
                                  'Blocks a Player picked up', "counter")
        player_deaths = Metric('player_deaths', 'How often a Player died',
                               "counter")
        player_jumps = Metric('player_jumps', 'How often a Player has jumped',
                              "counter")
        cm_traveled = Metric(
            'cm_traveled',
            'How many cm a Player traveled, whatever that means', "counter")
        player_xp_total = Metric('player_xp_total',
                                 "How much total XP a player has", "counter")
        player_current_level = Metric('player_current_level',
                                      "How much current XP a player has",
                                      "counter")
        player_food_level = Metric('player_food_level',
                                   "How much food the player currently has",
                                   "counter")
        player_health = Metric('player_health',
                               "How much Health the player currently has",
                               "counter")
        player_score = Metric('player_score', "The Score of the player",
                              "counter")
        entities_killed = Metric('entities_killed',
                                 "Entities killed by player", "counter")
        damage_taken = Metric('damage_taken', "Damage Taken by Player",
                              "counter")
        damage_dealt = Metric('damage_dealt', "Damage dealt by Player",
                              "counter")
        blocks_crafted = Metric('blocks_crafted', "Items a Player crafted",
                                "counter")
        player_playtime = Metric('player_playtime',
                                 "Time in Minutes a Player was online",
                                 "counter")
        player_advancements = Metric(
            'player_advancements', "Number of completed advances of a player",
            "counter")
        player_slept = Metric('player_slept', "Times a Player slept in a bed",
                              "counter")
        player_quests_finished = Metric(
            'player_quests_finished', 'Number of quests a Player has finished',
            'counter')
        player_used_crafting_table = Metric(
            'player_used_crafting_table',
            "Times a Player used a Crafting Table", "counter")
        mc_custom = Metric('mc_custom', "Custom Minectaft stat", "counter")
        for key, value in data.items():  # pre 1.15
            if key in ("stats", "DataVersion"):
                continue
            stat = key.split(".")[1]  # entityKilledBy
            if stat == "mineBlock":
                blocks_mined.add_sample("blocks_mined",
                                        value=value,
                                        labels={
                                            'player':
                                            name,
                                            'block':
                                            '.'.join((key.split(".")[2],
                                                      key.split(".")[3]))
                                        })
            elif stat == "pickup":
                blocks_picked_up.add_sample("blocks_picked_up",
                                            value=value,
                                            labels={
                                                'player':
                                                name,
                                                'block':
                                                '.'.join((key.split(".")[2],
                                                          key.split(".")[3]))
                                            })
            elif stat == "entityKilledBy":
                if len(key.split(".")) == 4:
                    player_deaths.add_sample('player_deaths',
                                             value=value,
                                             labels={
                                                 'player':
                                                 name,
                                                 'cause':
                                                 '.'.join((key.split(".")[2],
                                                           key.split(".")[3]))
                                             })
                else:
                    player_deaths.add_sample('player_deaths',
                                             value=value,
                                             labels={
                                                 'player': name,
                                                 'cause': key.split(".")[2]
                                             })
            elif stat == "jump":
                player_jumps.add_sample("player_jumps",
                                        value=value,
                                        labels={'player': name})
            elif stat == "walkOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "walking"
                                       })
            elif stat == "swimOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "swimming"
                                       })
            elif stat == "sprintOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "sprinting"
                                       })
            elif stat == "diveOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "diving"
                                       })
            elif stat == "fallOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "falling"
                                       })
            elif stat == "flyOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "flying"
                                       })
            elif stat == "boatOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "boat"
                                       })
            elif stat == "horseOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "horse"
                                       })
            elif stat == "climbOneCm":
                cm_traveled.add_sample("cm_traveled",
                                       value=value,
                                       labels={
                                           'player': name,
                                           'method': "climbing"
                                       })
            elif stat == "XpTotal":
                player_xp_total.add_sample('player_xp_total',
                                           value=value,
                                           labels={'player': name})
            elif stat == "XpLevel":
                player_current_level.add_sample('player_current_level',
                                                value=value,
                                                labels={'player': name})
            elif stat == "foodLevel":
                player_food_level.add_sample('player_food_level',
                                             value=value,
                                             labels={'player': name})
            elif stat == "Health":
                player_health.add_sample('player_health',
                                         value=value,
                                         labels={'player': name})
            elif stat == "Score":
                player_score.add_sample('player_score',
                                        value=value,
                                        labels={'player': name})
            elif stat == "killEntity":
                entities_killed.add_sample('entities_killed',
                                           value=value,
                                           labels={
                                               'player': name,
                                               "entity": key.split(".")[2]
                                           })
            elif stat == "damageDealt":
                damage_dealt.add_sample('damage_dealt',
                                        value=value,
                                        labels={'player': name})
            elif stat == "damageTaken":
                damage_dealt.add_sample('damage_taken',
                                        value=value,
                                        labels={'player': name})
            elif stat == "craftItem":
                blocks_crafted.add_sample('blocks_crafted',
                                          value=value,
                                          labels={
                                              'player':
                                              name,
                                              'block':
                                              '.'.join((key.split(".")[2],
                                                        key.split(".")[3]))
                                          })
            elif stat == "playOneMinute":
                player_playtime.add_sample('player_playtime',
                                           value=value,
                                           labels={'player': name})
            elif stat == "advancements":
                player_advancements.add_sample('player_advancements',
                                               value=value,
                                               labels={'player': name})
            elif stat == "sleepInBed":
                player_slept.add_sample('player_slept',
                                        value=value,
                                        labels={'player': name})
            elif stat == "craftingTableInteraction":
                player_used_crafting_table.add_sample(
                    'player_used_crafting_table',
                    value=value,
                    labels={'player': name})
            elif stat == "questsFinished":
                player_quests_finished.add_sample('player_quests_finished',
                                                  value=value,
                                                  labels={'player': name})

        if "stats" in data:  # Minecraft > 1.15
            if "minecraft:crafted" in data["stats"]:
                for block, value in data["stats"]["minecraft:crafted"].items():
                    blocks_crafted.add_sample('blocks_crafted',
                                              value=value,
                                              labels={
                                                  'player': name,
                                                  'block': block
                                              })
            if "minecraft:mined" in data["stats"]:
                for block, value in data["stats"]["minecraft:mined"].items():
                    blocks_mined.add_sample("blocks_mined",
                                            value=value,
                                            labels={
                                                'player': name,
                                                'block': block
                                            })
            if "minecraft:picked_up" in data["stats"]:
                for block, value in data["stats"]["minecraft:picked_up"].items(
                ):
                    blocks_picked_up.add_sample("blocks_picked_up",
                                                value=value,
                                                labels={
                                                    'player': name,
                                                    'block': block
                                                })
            if "minecraft:killed" in data["stats"]:
                for entity, value in data["stats"]["minecraft:killed"].items():
                    entities_killed.add_sample('entities_killed',
                                               value=value,
                                               labels={
                                                   'player': name,
                                                   "entity": entity
                                               })
            if "minecraft:killed_by" in data["stats"]:
                for entity, value in data["stats"][
                        "minecraft:killed_by"].items():
                    player_deaths.add_sample('player_deaths',
                                             value=value,
                                             labels={
                                                 'player': name,
                                                 'cause': entity
                                             })
            for stat, value in data["stats"]["minecraft:custom"].items():
                if stat == "minecraft:jump":
                    player_jumps.add_sample("player_jumps",
                                            value=value,
                                            labels={'player': name})
                elif stat == "minecraft:deaths":
                    player_deaths.add_sample('player_deaths',
                                             value=value,
                                             labels={'player': name})
                elif stat == "minecraft:damage_taken":
                    damage_taken.add_sample('damage_taken',
                                            value=value,
                                            labels={'player': name})
                elif stat == "minecraft:damage_dealt":
                    damage_dealt.add_sample('damage_dealt',
                                            value=value,
                                            labels={'player': name})
                elif stat == "minecraft:play_one_minute":
                    player_playtime.add_sample('player_playtime',
                                               value=value,
                                               labels={'player': name})
                elif stat == "minecraft:walk_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "walking"
                                           })
                elif stat == "minecraft:walk_on_water_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "swimming"
                                           })
                elif stat == "minecraft:sprint_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "sprinting"
                                           })
                elif stat == "minecraft:walk_under_water_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "diving"
                                           })
                elif stat == "minecraft:fall_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "falling"
                                           })
                elif stat == "minecraft:fly_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "flying"
                                           })
                elif stat == "minecraft:boat_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "boat"
                                           })
                elif stat == "minecraft:horse_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "horse"
                                           })
                elif stat == "minecraft:climb_one_cm":
                    cm_traveled.add_sample("cm_traveled",
                                           value=value,
                                           labels={
                                               'player': name,
                                               'method': "climbing"
                                           })
                elif stat == "minecraft:sleep_in_bed":
                    player_slept.add_sample('player_slept',
                                            value=value,
                                            labels={'player': name})
                elif stat == "minecraft:interact_with_crafting_table":
                    player_used_crafting_table.add_sample(
                        'player_used_crafting_table',
                        value=value,
                        labels={'player': name})
                else:
                    mc_custom.add_sample('mc_custom',
                                         value=value,
                                         labels={'stat': stat})
        return [
            blocks_mined, blocks_picked_up, player_deaths, player_jumps,
            cm_traveled, player_xp_total, player_current_level,
            player_food_level, player_health, player_score, entities_killed,
            damage_taken, damage_dealt, blocks_crafted, player_playtime,
            player_advancements, player_slept, player_used_crafting_table,
            player_quests_finished, mc_custom
        ]

    def collect(self):
        for player in self.get_players():
            for metric in self.update_metrics_for_player(player):
                yield metric
        for metric in self.get_server_stats():
            yield metric
Esempio n. 21
0
class MCServerAdmin:
    def __init__(self, ip=None, rcon_port=None, rcon_pass=None):
        self.tk_window = tk.Tk()
        self.tk_style = ttk.Style(self.tk_window)
        self.tk_window.withdraw()

        if not ip and not rcon_port and not rcon_pass:
            self.generate_login_ui()
        else:
            self.connectToServer(ip, rcon_port, rcon_pass)
            self.generate_main_ui()
        self.tk_window.mainloop()

    def generate_main_ui(self):
        self.tk_window.deiconify()
        self.tk_window.title(f"PyMineCraft Administration - {self.ip}:{self.port}")

        commandLabel = ttk.Label(self.tk_window, text="Enter a command: ")
        commandLabel.grid(column=0, row=0, padx=10, pady=10, sticky='w')
        commandInput = ttk.Entry(self.tk_window, width=50)
        commandInput.grid(column=0, row=1, padx=10, pady=10, sticky='w')
        sendCommandButton = ttk.Button(self.tk_window, command=lambda: self.processCommand(commandInput.get()) or commandInput.delete(0, len(commandInput.get())),
                                       text='Send Command')
        sendCommandButton.grid(column=0, row=2, padx=10, pady=10, sticky='w')
        self.tk_window.bind('<Return>', lambda x: self.processCommand(commandInput.get()) or commandInput.delete(0, len(commandInput.get())))

        serverResponseLabel = ttk.Label(self.tk_window, text="Server Response:")
        serverResponseLabel.grid(column=0, row=3, padx=10, pady=10, sticky='w')
        self.serverResponseText = ttk.Label(self.tk_window, text="N/A")
        self.serverResponseText.grid(column=0, row=4, padx=10, pady=10, sticky='NSEW')

    def generate_login_ui(self):
        self.tk_window.deiconify()
        self.tk_window.title("PyMineCraft Administration - Login")

        serverIPLabel = ttk.Label(self.tk_window, text="Minecraft Server IP: ")
        serverIPLabel.grid(column=0, row=0, padx=10, pady=10, sticky='w')
        serverIPInput = ttk.Entry(self.tk_window)
        serverIPInput.grid(column=0, row=1, padx=10, pady=10, sticky='w')
        rconPortLabel = ttk.Label(self.tk_window, text="RCON Port: ")
        rconPortLabel.grid(column=1, row=0, padx=10, pady=10, sticky='w')
        rconPortInput = ttk.Entry(self.tk_window)
        rconPortInput.grid(column=1, row=1, padx=10, pady=10, sticky='w')
        rconPasswordLabel = ttk.Label(self.tk_window, text="RCON Password: "******"Server Connectivity - ERROR", "The Minecraft Server RCON port must be an integer (1-65535)")
            raise ValueError("The Minecraft Server RCON port must be an integer (1-65535)")
        self.mcr_instance = MCRcon(host=self.ip, port=self.port, password=rcon_pass)
        self.mcr_instance.connect()
        if self.mcr_instance:
            messagebox.showinfo("Server Connectivity - Success", "Successfully connected to the server!")
            self.resetWindow()
            self.generate_main_ui()
        else:
            messagebox.showinfo("Server Connectivity - Failed", "Failed connecting to the server!")

    def resetWindow(self):
        self.tk_window.destroy()
        self.tk_window = tk.Tk()
        self.tk_style = ttk.Style(self.tk_window)

    def processCommand(self, command):
        if self.mcr_instance:
            cmd_input = command.strip()
            if len(cmd_input) == 0:
                return
            elif cmd_input == "!quit":
                self.exit()
                return
            elif cmd_input[0] != "/":
                resp = f"> {cmd_input}"
                cmd_input = f"say {cmd_input}"
                self.mcr_instance.command(cmd_input)
            else:
                cmd_input = cmd_input[1:]
                resp = self.mcr_instance.command(cmd_input)
            print(resp)
            if self.serverResponseText:
                self.serverResponseText.config(text=resp)

    def exit(self):
        if self.mcr_instance:
            self.mcr_instance.disconnect()
        if self.tk_window:
            self.tk_window.destroy()
    def get_server_stats(self):
        if not all(x in os.environ for x in ['RCON_HOST', 'RCON_PASSWORD']):
            return []
        dim_tps = Metric('dim_tps', 'TPS of a dimension', "counter")
        dim_ticktime = Metric('dim_ticktime',
                              "Time a Tick took in a Dimension", "counter")
        overall_tps = Metric('overall_tps', 'overall TPS', "counter")
        overall_ticktime = Metric('overall_ticktime', "overall Ticktime",
                                  "counter")
        player_online = Metric('player_online', "is 1 if player is online",
                               "counter")
        entities = Metric('entities', "type and count of active entites",
                          "counter")
        mcr = MCRcon(os.environ['RCON_HOST'],
                     os.environ['RCON_PASSWORD'],
                     port=int(os.environ['RCON_PORT']))
        mcr.connect()

        # dimensions
        resp = mcr.command("forge tps")
        dimtpsregex = re.compile(
            "Dim\s*(-*\d*)\s\((.*?)\)\s:\sMean tick time:\s(.*?) ms\. Mean TPS: (\d*\.\d*)"
        )
        for dimid, dimname, meanticktime, meantps in dimtpsregex.findall(resp):
            dim_tps.add_sample('dim_tps',
                               value=meantps,
                               labels={
                                   'dimension_id': dimid,
                                   'dimension_name': dimname
                               })
            dim_ticktime.add_sample('dim_ticktime',
                                    value=meanticktime,
                                    labels={
                                        'dimension_id': dimid,
                                        'dimension_name': dimname
                                    })
        overallregex = re.compile(
            "Overall\s?: Mean tick time: (.*) ms. Mean TPS: (.*)")
        overall_tps.add_sample('overall_tps',
                               value=overallregex.findall(resp)[0][1],
                               labels={})
        overall_ticktime.add_sample('overall_ticktime',
                                    value=overallregex.findall(resp)[0][0],
                                    labels={})

        # entites
        resp = mcr.command("forge entity list")
        entityregex = re.compile("(\d+): (.*?:.*?)\s")
        for entitycount, entityname in entityregex.findall(resp):
            entities.add_sample('entities',
                                value=entitycount,
                                labels={'entity': entityname})

        # player
        resp = mcr.command("list")
        playerregex = re.compile("There are \d*\/20 players online:(.*)")
        if playerregex.findall(resp):
            for player in playerregex.findall(resp)[0].split(","):
                if player:
                    player_online.add_sample(
                        'player_online',
                        value=1,
                        labels={'player': player.lstrip()})

        return [
            dim_tps, dim_ticktime, overall_tps, overall_ticktime,
            player_online, entities
        ]
Esempio n. 23
0
class RCON(commands.Cog):
    """Interface with a Minecraft server theorugh Discord with RCON"""
    def __init__(self, bot):
        self.bot = bot
        self.rcon = MCRcon(RCON_ADDR, RCON_PASS, RCON_PORT)
        self.rcon.connect()
        self.am = AccountManager()

    @commands.command(name="rcon")
    @commands.has_any_role(*ADMIN_ROLES)
    async def rcon_send(self, ctx, *command):
        command = " ".join(command)
        resp = self.rcon.command(command)

        embed = discord.Embed(title="RCON",
                              description=f"Command: {command}",
                              colour=0x0FFF0F)
        embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url)
        data = resp if resp else "No data was returned. This likely means the command succeeded."
        embed.add_field(name="Result", value=data)

        await ctx.channel.send(embed=embed)

    @commands.command(name="whitelist")
    async def rcon_whitelist(self, ctx, username: str):
        roles = [role.name for role in ctx.author.roles]
        force = any([role in BYPASS_ROLES for role in roles])
        result, data = self.am.whitelist_add(username,
                                             ctx.author.id,
                                             override=force)
        resp = None

        if result:
            resp = self.rcon.command(f"whitelist add {username}")
        else:
            resp = data

        embed = discord.Embed(
            title="Whitelist",
            description=f"User: {username} ({ctx.author.id})",
            colour=0x0F0FFF)
        embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url)
        embed.add_field(name="Result", value=resp)

        await ctx.channel.send(embed=embed)

    @commands.command(name="unwhitelist")
    @commands.has_any_role(*ADMIN_ROLES)
    async def rcon_unwhitelist(self, ctx, username: str):
        result, data = self.am.whitelist_remove(username)
        resp = None

        if result:
            resp = self.rcon.command(f"whitelist remove {username}")
        else:
            resp = data

        embed = discord.Embed(title="Unwhitelist",
                              description=f"User: {username}",
                              colour=0xFF0F0F)
        embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url)
        embed.add_field(name="Result", value=resp)

        await ctx.channel.send(embed=embed)

    @commands.command(name="purgeuser")
    @commands.has_any_role(*ADMIN_ROLES)
    async def rcon_purge(self, ctx, username):
        result, data = self.am.whitelist_remove(username)

        resp = self.rcon.command(f"whitelist remove {username}")

        embed = discord.Embed(title="Purge",
                              description=f"User: {username}",
                              colour=0xFF0F0F)
        embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url)
        embed.add_field(name="WLDB Result", value=data)
        embed.add_field(name="RCON Result", value=resp)

        await ctx.channel.send(embed=embed)
Esempio n. 24
0
class MainCog(commands.Cog, name="main"):
    def __init__(self, bot: DioCraft):
        super().__init__()

        self.mcr = MCRcon(server_ip, server_password, int(server_port))

        try:
            self.mcr.connect()
        except:
            print("Unexpected error: {}".format(sys.exc_info()[0]))
            self.mcr.disconnect()

        self.bot = bot

    @commands.command()
    async def login(self, ctx: commands.Command):
        allowed_roles = ["Admins"]
        if (await self.privilegeCheck(ctx, allowed_roles)
                and await self.channelCheck(ctx)):
            channel = self.bot.get_channel(729513577186066473)
            try:
                self.mcr.connect()
                await channel.send(
                    "Successfully logged into the Minecraft server.")
            except:
                await channel.send("Unexpected error: {}".format(
                    sys.exc_info()[0]))
                self.mcr.disconnect()

    @commands.command()
    async def awl(self, ctx: commands.Command):
        allowed_roles = ["Minecraft", "Admins"]
        if (await self.privilegeCheck(ctx, allowed_roles)
                and await self.channelCheck(ctx)):
            player_name = ctx.message.content[5:]
            resp = self.mcr.command("/whitelist add {}".format(player_name))
            await ctx.send(resp)

    @commands.command()
    async def dwl(self, ctx: commands.Command):
        allowed_roles = ["Admins"]
        if (await self.privilegeCheck(ctx, allowed_roles)
                and await self.channelCheck(ctx)):
            player_name = ctx.message.content[5:]
            resp = self.mcr.command("/whitelist remove {}".format(player_name))
            await ctx.send(resp)

    @commands.command()
    async def wl(self, ctx: commands.Command):
        if (await self.channelCheck(ctx)):
            await self.listWhitelist(ctx)

    @commands.command()
    async def online(self, ctx: commands.Command):
        if (await self.channelCheck(ctx)):
            await self.listPlayers(ctx)

    @commands.command()
    async def help(self, ctx: commands.Command):
        if (await self.channelCheck(ctx)):
            msg = "Thank you for using DioCraft! It is still currently under development :)\n\n"
            msg += "/online - Display list of all players that are online.\n"
            msg += "/wl - Display list of all players that are whitelisted.\n"
            msg += "/awl <name> - To add someone to the server whitelist.\n"
            msg += "/dwl <name> - To remove someone from the server whitelist.\n\n"
            msg += "/login - For administrators to connect to the server.\n\n"
            msg += "If you have any questions or suggestions, please contact primal#7602! Thank you!"
            await ctx.send(msg)

    async def privilegeCheck(self, ctx: commands.Command, allowed_roles):
        is_admin = False

        for role in ctx.message.author.roles:
            if (role.name in allowed_roles):
                is_admin = True

        if (not is_admin):
            await ctx.send("{}, does not have permission.".format(
                ctx.message.author.display_name))

        return is_admin

    async def channelCheck(self, ctx: commands.Command):
        is_channel = False
        allowed_channels = [729859164209283104, 729513577186066473]
        # Change this to list to hold more channels.
        if (ctx.message.channel.id in allowed_channels):
            is_channel = True

        return is_channel

    async def listWhitelist(self, ctx: commands.Command):
        resp = self.mcr.command("/whitelist list").split(" ")
        result = "```The following {} players are whitelisted:\n".format(
            resp[2])
        resp = resp[5:]
        lastName = resp[len(resp) - 1]
        resp = resp[:-1]

        for name in resp:
            result += name[:-1] + "\n"

        result += lastName + "```"

        await ctx.send(result)

    async def listPlayers(self, ctx: commands.Command):
        resp = self.mcr.command("/list").split(" ")
        result = "```There {} players online:\n".format(resp[2])
        resp = resp[10:]
        lastName = resp[len(resp) - 1]
        resp = resp[:-1]

        for name in resp:
            result += name[:-1] + "\n"

        result += lastName + "```"

        await ctx.send(result)
Esempio n. 25
0
from mcrcon import MCRcon
import constants
import utils
import time
import requests
import subprocess
import re
import json
import random
import threading

rcon = MCRcon('localhost', 'rcon')
rcon.connect()

setattr(rcon, 'say', lambda s: rcon.command(f'say {s}'))

with open('warps.json') as fp:
    warps = json.load(fp)


def write_warps():
    with open('warps.json', 'w+') as fp:
        json.dump(warps, fp)


def command_exec(name: str, args: str):
    rcon.say(utils.exec_python(args, locals(), globals()))


def command_joke(name: str, args: str):
    rcon.say(utils.get_joke())
Esempio n. 26
0
class MinecraftCollector(object):
    def __init__(self):
        prefix="/home/jiba/Minecraft/server"
        self.statsdirectory = prefix+"/world/stats"
        self.playerdirectory = prefix+"/world/playerdata"
        self.advancementsdirectory = prefix+"/world/advancements"
        self.betterquesting = prefix+"/world/betterquesting"
        self.map = dict()
        self.questsEnabled = False
        if all(x in os.environ for x in ['RCON_HOST','RCON_PASSWORD']):
            self.mcr = MCRcon(os.environ['RCON_HOST'],os.environ['RCON_PASSWORD'],port=int(os.environ['RCON_PORT']))
            self.mcr.connect()
        if os.path.isdir(self.betterquesting):
            self.questsEnabled = True

    def get_players(self):
        return [f[:-5] for f in listdir(self.statsdirectory) if isfile(join(self.statsdirectory, f))]

    def uuid_to_player(self,uuid):
        uuid = uuid.replace('-','')
        if uuid in self.map:
            return self.map[uuid]
        else:
            result = requests.get('https://api.mojang.com/user/profiles/'+uuid+'/names')
            self.map[uuid] = result.json()[0]['name']
            return(result.json()[0]['name'])

    def get_server_stats(self):
        if not all(x in os.environ for x in ['RCON_HOST','RCON_PASSWORD']):
            return []
        dim_tps          = Metric('dim_tps','TPS of a dimension',"counter")
        dim_ticktime     = Metric('dim_ticktime',"Time a Tick took in a Dimension","counter")
        overall_tps      = Metric('overall_tps','overall TPS',"counter")
        overall_ticktime = Metric('overall_ticktime',"overall Ticktime","counter")
        player_online    = Metric('player_online',"is 1 if player is online","counter")
        entities         = Metric('entities',"type and count of active entites", "counter")
        #moded by AceDroidX
        tps_from_last1    = Metric('tps_from_last1',"tps_from_last 1 min(tps)", "gauge")
        tps_from_last5    = Metric('tps_from_last5',"tps_from_last 5 min(tps)", "gauge")
        tps_from_last15    = Metric('tps_from_last15',"tps_from_last 15 min(tps)", "gauge")

        resp = self.mcr.command("tps")
        print('debug:tps'+resp)
        dimtpsregex = re.compile("([0-9]*\..*), §.*?([0-9]*\..*), §.*?([0-9]*\..*)")
        print('debug:tpsfindall:'+str(dimtpsregex.findall(resp)))
        tps_from_last1.add_sample('tps_from_last1',value=dimtpsregex.findall(resp)[0][0],labels={})
        tps_from_last5.add_sample('tps_from_last5',value=dimtpsregex.findall(resp)[0][1],labels={})
        tps_from_last15.add_sample('tps_from_last15',value=dimtpsregex.findall(resp)[0][2],labels={})

        # dimensions
        """
        resp = mcr.command("forge tps")
        print('debug1'+resp)
        dimtpsregex = re.compile("Dim\s*(-*\d*)\s\((.*?)\)\s:\sMean tick time:\s(.*?) ms\. Mean TPS: (\d*\.\d*)")
        for dimid, dimname, meanticktime, meantps in dimtpsregex.findall(resp):
            dim_tps.add_sample('dim_tps',value=meantps,labels={'dimension_id':dimid,'dimension_name':dimname})
            dim_ticktime.add_sample('dim_ticktime',value=meanticktime,labels={'dimension_id':dimid,'dimension_name':dimname})
        overallregex = re.compile("Overall\s?: Mean tick time: (.*) ms. Mean TPS: (.*)")
        overall_tps.add_sample('overall_tps',value=overallregex.findall(resp)[0][1],labels={})
        overall_ticktime.add_sample('overall_ticktime',value=overallregex.findall(resp)[0][0],labels={})
        """

        # entites
        """
        resp = mcr.command("forge entity list")
        entityregex = re.compile("(\d+): (.*?:.*?)\s")
        for entitycount, entityname in entityregex.findall(resp):
            entities.add_sample('entities',value=entitycount,labels={'entity':entityname})
        """

        # player
        resp = self.mcr.command("list")
        playerregex = re.compile("There are \d*\/20 players online:(.*)")
        if playerregex.findall(resp):
            for player in playerregex.findall(resp)[0].split(","):
                if player:
                    player_online.add_sample('player_online',value=1,labels={'player':player.lstrip()})

        #return[dim_tps,dim_ticktime,overall_tps,overall_ticktime,player_online,entities]
        return[player_online,tps_from_last1,tps_from_last5,tps_from_last15]

    def get_player_quests_finished(self,uuid):
        with open(self.betterquesting+"/QuestProgress.json") as json_file:
            data = json.load(json_file)
            json_file.close()
        counter = 0
        for _, value in data['questProgress:9'].items():
            for _, u in value['tasks:9']['0:10']['completeUsers:9'].items():
                if u == uuid:
                    counter +=1
        return counter

    def get_player_stats(self,uuid):
        with open(self.statsdirectory+"/"+uuid+".json") as json_file:
            data = json.load(json_file)
            json_file.close()
        nbtfile = nbt.nbt.NBTFile(self.playerdirectory+"/"+uuid+".dat",'rb')
        data["stat.XpTotal"]  = nbtfile.get("XpTotal").value
        data["stat.XpLevel"]  = nbtfile.get("XpLevel").value
        data["stat.Score"]    = nbtfile.get("Score").value
        data["stat.Health"]   = nbtfile.get("Health").value
        data["stat.foodLevel"]= nbtfile.get("foodLevel").value
        with open(self.advancementsdirectory+"/"+uuid+".json") as json_file:
            count = 0
            advancements = json.load(json_file)
            for key, value in advancements.items():
                if key in ("DataVersion"):
                  continue
                if value["done"] == True:
                    count += 1
        data["stat.advancements"] = count
        if self.questsEnabled:
            data["stat.questsFinished"] = self.get_player_quests_finished(uuid)
        return data

    def update_metrics_for_player(self,uuid):
        data = self.get_player_stats(uuid)
        name = self.uuid_to_player(uuid)
        blocks_mined        = Metric('blocks_mined','Blocks a Player mined',"counter")
        blocks_picked_up    = Metric('blocks_picked_up','Blocks a Player picked up',"counter")
        player_deaths       = Metric('player_deaths','How often a Player died',"counter")
        player_jumps        = Metric('player_jumps','How often a Player has jumped',"counter")
        cm_traveled         = Metric('cm_traveled','How many cm a Player traveled, whatever that means',"counter")
        player_xp_total     = Metric('player_xp_total',"How much total XP a player has","counter")
        player_current_level= Metric('player_current_level',"How much current XP a player has","counter")
        player_food_level   = Metric('player_food_level',"How much food the player currently has","counter")
        player_health       = Metric('player_health',"How much Health the player currently has","counter")
        player_score        = Metric('player_score',"The Score of the player","counter")
        entities_killed     = Metric('entities_killed',"Entities killed by player","counter")
        damage_taken        = Metric('damage_taken',"Damage Taken by Player","counter")
        damage_dealt        = Metric('damage_dealt',"Damage dealt by Player","counter")
        blocks_crafted      = Metric('blocks_crafted',"Items a Player crafted","counter")
        player_playtime     = Metric('player_playtime',"Time in Minutes a Player was online","counter")
        player_advancements = Metric('player_advancements', "Number of completed advances of a player","counter")
        player_slept        = Metric('player_slept',"Times a Player slept in a bed","counter")
        player_quests_finished = Metric('player_quests_finished', 'Number of quests a Player has finished', 'counter')
        player_used_crafting_table = Metric('player_used_crafting_table',"Times a Player used a Crafting Table","counter")
        for key, value in data.items():
            if key in ("stats", "DataVersion"):
                continue
            stat = key.split(".")[1] # entityKilledBy
            if stat == "mineBlock":
                blocks_mined.add_sample("blocks_mined",value=value,labels={'player':name,'block':'.'.join((key.split(".")[2],key.split(".")[3]))})
            elif stat == "pickup":
                blocks_picked_up.add_sample("blocks_picked_up",value=value,labels={'player':name,'block':'.'.join((key.split(".")[2],key.split(".")[3]))})
            elif stat == "entityKilledBy":
                if len(key.split(".")) == 4:
                    player_deaths.add_sample('player_deaths',value=value,labels={'player':name,'cause':'.'.join((key.split(".")[2],key.split(".")[3]))})
                else:
                    player_deaths.add_sample('player_deaths',value=value,labels={'player':name,'cause':key.split(".")[2]})
            elif stat == "jump":
                player_jumps.add_sample("player_jumps",value=value,labels={'player':name})
            elif stat == "walkOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"walking"})
            elif stat == "swimOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"swimming"})
            elif stat == "sprintOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"sprinting"})
            elif stat == "diveOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"diving"})
            elif stat == "fallOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"falling"})
            elif stat == "flyOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"flying"})
            elif stat == "boatOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"boat"})
            elif stat == "horseOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"horse"})
            elif stat == "climbOneCm":
                cm_traveled.add_sample("cm_traveled",value=value,labels={'player':name,'method':"climbing"})
            elif stat == "XpTotal":
                player_xp_total.add_sample('player_xp_total',value=value,labels={'player':name})
            elif stat == "XpLevel":
                player_current_level.add_sample('player_current_level',value=value,labels={'player':name})
            elif stat == "foodLevel":
                player_food_level.add_sample('player_food_level',value=value,labels={'player':name})
            elif stat == "Health":
                player_health.add_sample('player_health',value=value,labels={'player':name})
            elif stat == "Score":
                player_score.add_sample('player_score',value=value,labels={'player':name})
            elif stat == "killEntity":
                entities_killed.add_sample('entities_killed',value=value,labels={'player':name,"entity":key.split(".")[2]})
            elif stat == "damageDealt":
                damage_dealt.add_sample('damage_dealt',value=value,labels={'player':name})
            elif stat == "damageTaken":
                damage_dealt.add_sample('damage_taken',value=value,labels={'player':name})
            elif stat == "craftItem":
                blocks_crafted.add_sample('blocks_crafted',value=value,labels={'player':name,'block':'.'.join((key.split(".")[2],key.split(".")[3]))})
            elif stat == "playOneMinute":
                player_playtime.add_sample('player_playtime',value=value,labels={'player':name})
            elif stat == "advancements":
                player_advancements.add_sample('player_advancements',value=value,labels={'player':name})
            elif stat == "sleepInBed":
                player_slept.add_sample('player_slept',value=value,labels={'player':name})
            elif stat == "craftingTableInteraction":
                player_used_crafting_table.add_sample('player_used_crafting_table',value=value,labels={'player':name})
            elif stat == "questsFinished":
                player_quests_finished.add_sample('player_quests_finished',value=value,labels={'player':name})
        return [blocks_mined,blocks_picked_up,player_deaths,player_jumps,cm_traveled,player_xp_total,player_current_level,player_food_level,player_health,player_score,entities_killed,damage_taken,damage_dealt,blocks_crafted,player_playtime,player_advancements,player_slept,player_used_crafting_table,player_quests_finished]

    def collect(self):
#        for player in self.get_players():
#            for metric in self.update_metrics_for_player(player)+self.get_server_stats():
#                yield metric
         for metric in self.get_server_stats():
             yield metric
    def get_server_stats(self):
        if not all(x in os.environ for x in ['RCON_HOST', 'RCON_PASSWORD']):
            return []
        dim_tps = Metric('dim_tps', 'TPS of a dimension', "counter")
        dim_ticktime = Metric('dim_ticktime',
                              "Time a Tick took in a Dimension", "counter")
        overall_tps = Metric('overall_tps', 'overall TPS', "counter")
        overall_ticktime = Metric('overall_ticktime', "overall Ticktime",
                                  "counter")
        player_online = Metric('player_online', "is 1 if player is online",
                               "counter")
        entities = Metric('entities', "type and count of active entites",
                          "counter")
        mcr = MCRcon(os.environ['RCON_HOST'],
                     os.environ['RCON_PASSWORD'],
                     port=int(os.environ['RCON_PORT']))
        mcr.connect()

        # dimensions
        resp = mcr.command("forge tps")
        dimtpsregex = re.compile(
            "Dim\s*(-*\d*)\s\((.*?)\)\s:\sMean tick time:\s(.*?) ms\. Mean TPS: (\d*\.\d*)"
        )
        for dimid, dimname, meanticktime, meantps in dimtpsregex.findall(resp):
            dim_tps.add_sample('dim_tps',
                               value=meantps,
                               labels={
                                   'dimension_id': dimid,
                                   'dimension_name': dimname
                               })
            dim_ticktime.add_sample('dim_ticktime',
                                    value=meanticktime,
                                    labels={
                                        'dimension_id': dimid,
                                        'dimension_name': dimname
                                    })
        overallregex = re.compile(
            "Overall\s?: Mean tick time: (.*) ms. Mean TPS: (.*)")
        overall_tps.add_sample('overall_tps',
                               value=overallregex.findall(resp)[0][1],
                               labels={})
        overall_ticktime.add_sample('overall_ticktime',
                                    value=overallregex.findall(resp)[0][0],
                                    labels={})

        # dynmap
        if os.environ['DYNMAP_ENABLED'] == "True":
            dynmap_tile_render_statistics = Metric(
                'dynmap_tile_render_statistics',
                'Tile Render Statistics reported by Dynmap', "counter")
            dynmap_chunk_loading_statistics_count = Metric(
                'dynmap_chunk_loading_statistics_count',
                'Chunk Loading Statistics reported by Dynmap', "counter")
            dynmap_chunk_loading_statistics_duration = Metric(
                'dynmap_chunk_loading_statistics_duration',
                'Chunk Loading Statistics reported by Dynmap', "counter")

            resp = mcr.command("dynmap stats")

            dynmaptilerenderregex = re.compile(
                "  (.*?): processed=(\d*), rendered=(\d*), updated=(\d*)")
            for dim, processed, rendered, updated in dynmaptilerenderregex.findall(
                    resp):
                dynmap_tile_render_statistics.add_sample(
                    'dynmap_tile_render_statistics',
                    value=processed,
                    labels={
                        'type': 'processed',
                        'file': dim
                    })
                dynmap_tile_render_statistics.add_sample(
                    'dynmap_tile_render_statistics',
                    value=rendered,
                    labels={
                        'type': 'rendered',
                        'file': dim
                    })
                dynmap_tile_render_statistics.add_sample(
                    'dynmap_tile_render_statistics',
                    value=updated,
                    labels={
                        'type': 'updated',
                        'file': dim
                    })

            dynmapchunkloadingregex = re.compile(
                "Chunks processed: (.*?): count=(\d*), (\d*.\d*)")
            for state, count, duration_per_chunk in dynmapchunkloadingregex.findall(
                    resp):
                dynmap_chunk_loading_statistics_count.add_sample(
                    'dynmap_chunk_loading_statistics',
                    value=count,
                    labels={'type': state})
                dynmap_chunk_loading_statistics_duration.add_sample(
                    'dynmap_chunk_loading_duration',
                    value=duration_per_chunk,
                    labels={'type': state})

        # entites
        resp = mcr.command("forge entity list")
        entityregex = re.compile("(\d+): (.*?:.*?)\s")
        for entitycount, entityname in entityregex.findall(resp):
            entities.add_sample('entities',
                                value=entitycount,
                                labels={'entity': entityname})

        # player
        resp = mcr.command("list")
        playerregex = re.compile("There are \d*\/20 players online:(.*)")
        if playerregex.findall(resp):
            for player in playerregex.findall(resp)[0].split(","):
                if player:
                    player_online.add_sample(
                        'player_online',
                        value=1,
                        labels={'player': player.lstrip()})

        return [
            dim_tps, dim_ticktime, overall_tps, overall_ticktime,
            player_online, entities, dynmap_tile_render_statistics,
            dynmap_chunk_loading_statistics_count,
            dynmap_chunk_loading_statistics_duration
        ]
Esempio n. 28
0
class MinecraftStatus():
    def __init__(self, serverUrl, localIp, rconPort, queryPort, rconPassword):
        self.serverUrl = serverUrl
        self.localServer = MinecraftServer(localIp, queryPort)
        self.urlServer = MinecraftServer(serverUrl, queryPort)
        self.rcon = MCRcon(localIp, rconPassword, port=rconPort)
        self.previousPlayerAmountOnline = None
        self.rconConnect()

    def rconConnect(self):
        try:
            self.rcon.connect()
        except ConnectionRefusedError:
            logger.error("RCON Connection refused")
        except:
            logger.error("Unexpected error while trying to connect to RCON")

    def generateStatus(self):
        tps = ""
        urlLatency = -1
        localLatency = -1
        playerAmountOnline = -1
        maxPlayerAmount = -1
        playerList = ""
        mapName = ""

        try:
            urlStatus = self.urlServer.status()
            urlLatency = str(urlStatus.latency)
        except:
            logger.error("Error while contacting server over url")

        try:
            localStatus = self.localServer.status()
            localLatency = str(localStatus.latency)
            playerAmountOnline = localStatus.players.online
            maxPlayerAmount = localStatus.players.max
            players = localStatus.players.sample
            if playerAmountOnline > 0:
                for player in players:
                    playerList += player.name + ", "
                playerList = playerList[:-2]  # Remove last comma
                playerList += "."
        except:
            logger.error("Error getting local server status")

        try:
            tps = self.rcon.command("tps")
        except:
            logger.error("Rcon connection failed")
            self.rconConnect()
        tps = tps[29:]
        tps = tps.replace('§a', '')

        response = "```"
        response += 'Status report for ' + self.serverUrl + ': \n'
        if urlLatency != -1:
            response += "The server replied over DNS in " + \
                str(urlLatency) + 'ms\n'
        else:
            response += "The server did not reply over DNS\n"
        if localLatency != -1:
            response += "The server replied over the local network in " + \
                str(localLatency) + " ms\n"
            if playerAmountOnline > 0:
                response += "The server has " + \
                    str(playerAmountOnline) + "/" + \
                    str(maxPlayerAmount) + " players online.\n"
                response += "Online players: " + playerList + "\n"
            else:
                response += "No players are currently playing. Please do something about that :)\n"
            response += "The TPS for the server (1m,5m,15m) are: " + tps + "\n"
        else:
            response += "The server did not reply over the local network\n"
        response += "```"
        return response

    async def watch(self, sendNotifications):
        if self.previousPlayerAmountOnline is None:
            self.previousPlayerAmountOnline = self.localServer.status(
            ).players.online
            logger.debug("Initting prev players online")
        while True:  # TODO: add stop flag
            localStatus = self.localServer.status()
            playerAmountOnline = localStatus.players.online
            if playerAmountOnline != self.previousPlayerAmountOnline:
                logger.debug("Playercount changed!")
                self.previousPlayerAmountOnline = playerAmountOnline
                if playerAmountOnline == 1:
                    await sendNotifications(
                        "Someone started playing on the server :D")
                elif playerAmountOnline == 0:
                    await sendNotifications(
                        "Awh, the server is all empty now :(")
            await asyncio.sleep(0.5)

    def say(self, message):
        self.command("say " + message)

    def command(self, message):
        try:
            self.rcon.command(message)
        except BrokenPipeError:
            logger.error("No Pipe for RCON command")
            self.rconConnect()
Esempio n. 29
0
class worldeatercommands(commands.Cog):
    def __init__(self, client):
        self.client = client
        self.rcon_smp = MCRcon(config_rcon['rcon_smp']['rcon-ip'],
                               config_rcon['rcon_smp']['rcon-password'],
                               config_rcon['rcon_smp']['rcon-port'])
        self.coords = ()
        self.peri_size = 0
        self.worldeater_crashed = False
        self.we_updates = False
        self.ylevel = 0

    @commands.Cog.listener()
    async def on_ready(self):
        print('worldeatercommands is online.')
        self.guild = self.client.get_guild(config_discord['guild'])
        self.we_channel = discord.utils.get(
            self.guild.text_channels, id=config_discord["worldeater_channel"])

    @commands.command(
        help=
        'Use this to start the worldeater script. Arguments: peri_size , mandatory<x,z>. x and z argument: Postion on any part of worldeater with no blocks above it. Height control is now necessary'
    )
    @commands.has_any_role('Member', 'Trial-Member')
    async def westart(self, ctx, peri_size: int, *coords):
        if self.check_worldeater.is_running():
            await ctx.send(embed=discord.Embed(
                title='World eater is already running', color=0xFF0000))
            return
        self.peri_size = peri_size
        if len(coords) == 2:
            self.coords = coords
            self.rcon_smp.connect()
            resp = self.rcon_smp.command(
                f'/script run top(\'surface\',{self.coords[0]}, 0, {self.coords[1]})'
            )
            self.ylevel = int(resp.split()[1]) + 1
            cycletime = peri_size / 2
            self.check_worldeater.change_interval(seconds=cycletime)
            self.check_worldeater.start()
            title = 'Worldeater is now running'
            msg = f'The peri size is: {peri_size}\n' \
                  f'Coordinates for height control: x={coords[0]} y={coords[1]}'

        else:
            self.coords = ()
            title = 'Command was done incorrectly'
            msg = f'You are running without height control'
        await ctx.send(
            embed=discord.Embed(title=title, colour=0xFF0000, description=msg))

    @commands.command(help='stops the world eater script')
    @commands.has_any_role('Member', 'Trial-Member')
    async def westop(self, ctx):
        if not self.check_worldeater.is_running():
            await ctx.send(embed=discord.Embed(
                title='No world eater is running', color=0xFF0000))
            return
        self.check_worldeater.cancel()
        self.check_worldeater.stop()
        self.coords = ()
        self.peri_size = 0
        self.worldeater_crashed = False
        self.we_updates = False
        self.ylevel = 0
        await ctx.send(embed=discord.Embed(
            title=f'World eater script is stopped now',
            colour=0x00FF00,
        ))

    @commands.command(help='use this to get info about the world eater')
    @commands.has_any_role('Member', 'Trial-Member')
    async def westatus(self, ctx):
        if not self.check_worldeater.is_running():
            await ctx.send(embed=discord.Embed(
                title='No world eater is running', color=0xFF0000))
            return

        if self.worldeater_crashed:
            title = 'World eater is stuck'
            color = 0xFF0000
        else:
            title = 'World eater is running'
            color = 0x00FF00

        if not self.coords:
            self.rcon_smp.connect()
            resp = self.rcon_smp.command(
                f'/script run reduce(last_tick_times(),_a+_,0)/100;')
            mspt = float(resp.split()[1])
            msg = f'MSPT is ~{round((mspt),1)}\n' \
                   'You are running without height control'
        else:
            self.rcon_smp.connect()
            resp = self.rcon_smp.command(
                f'/script run top(\'surface\',{self.coords[0]}, 0, {self.coords[1]})'
            )
            yLevel = int(resp.split()[1]) + 1
            timeleft = str(
                datetime.timedelta(seconds=(self.peri_size / 2) *
                                   (yLevel - 5)))
            self.rcon_smp.connect()
            resp = self.rcon_smp.command(
                f'/script run reduce(last_tick_times(),_a+_,0)/100;')
            mspt = float(resp.split()[1])
            msg = f'y-level: ~{yLevel}\n' \
                  f'WE has to run for another ~{timeleft}\n' \
                  f'MSPT is ~{round((mspt),1)}'
            self.rcon_smp.disconnect()
        await ctx.send(
            embed=discord.Embed(title=title, colour=color, description=msg))

    @commands.command(
        help=
        'get or remove worldeater role. you get pinged if worldeater crashes')
    @commands.has_any_role('Member', 'Trial-Member')
    async def wehelper(self, ctx):
        we_role = get(self.guild.roles, name=config_discord['worldeater_role'])
        if not we_role in ctx.message.author.roles:
            await ctx.message.author.add_roles(we_role)
            await ctx.send('You now get pinged if a world eater crashes')
        else:
            await ctx.message.author.remove_roles(we_role)
            await ctx.send('You are no longer a world eater helper')

    @commands.command(help='get live updates on world eater progress')
    @commands.has_any_role('Member', 'Trial-Member')
    async def weupdates(self, ctx):
        if not self.check_worldeater.is_running():
            await ctx.send(embed=discord.Embed(
                title='No world eater is running', color=0xFF0000))
            return
        if self.we_updates:
            await ctx.send(embed=discord.Embed(title='Live updates turned off',
                                               color=0xFF0000))
        else:
            await ctx.send(embed=discord.Embed(title='Live updates turned on',
                                               color=0x00FF00))
        self.we_updates = not self.we_updates

    @tasks.loop()
    async def check_worldeater(self):
        self.rcon_smp.connect()
        self.ylevel = yLevel
        resp = self.rcon_smp.command(
            f'/script run top(\'surface\',{self.coords[0]}, 0, {self.coords[1]})'
        )
        resp = int(resp.split()[1]) + 1
        if resp < ylevel:
            self.ylevel = ylevel - 1
            if not resp < yLevel:
                if not self.worldeater_crashed:
                    await asyncio.sleep(20)
                    self.rcon_smp.connect()
                    resp = self.rcon_smp.command(
                        f'/script run top(\'surface\',{self.coords[0]}, 0, {self.coords[1]})'
                    )
                    resp = int(resp.split()[1]) + 1
                    if not resp < ylevel:
                        role = get(self.guild.roles,
                                   name=config_discord['worldeater_role'])
                        await self.we_channel.send(
                            f'{role.mention} World eater is stuck.')
                        self.worldeater_crashed = True
                        self.check_worldeater.change_interval(seconds=10)
            elif self.worldeater_crashed:
                self.worldeater_crashed = False
                self.check_worldeater.change_interval(seconds=self.peri_size /
                                                      2)
                self.ylevel = ylevel - 1
                await self.we_channel.send(f'World eater is fine again.')
            if self.we_updates and not self.worldeater_crashed:
                if not self.coords:
                    msg = 'still running'
                else:
                    self.rcon_smp.connect()
                    resp = self.rcon_smp.command(
                        f'/script run top(\'surface\',{self.coords[0]}, 0, {self.coords[1]})'
                    )
                    yLevel = int(resp.split()[1]) + 1
                    timeleft = str(
                        datetime.timedelta(seconds=(self.peri_size / 2) *
                                           (yLevel - 5)))
                    self.rcon_smp.connect()
                    resp = self.rcon_smp.command(
                        f'/script run reduce(last_tick_times(),_a+_,0)/100;')
                    mspt = float(resp.split()[1])
                    msg = f'y-level: ~{yLevel}\n' \
                          f'WE has to run for another ~{timeleft}\n' \
                          f'MSPT is ~{round((mspt), 1)}'
                    self.rcon_smp.disconnect()
                await self.we_channel.send(embed=discord.Embed(
                    title='WE Updates', colour=0x00FF00, description=msg))
            self.rcon_smp.disconnect()
Esempio n. 30
0
def update_player_numbers(config):
    new_statuses = {}

    for server in config['servers'].keys():

        host = config['servers'][server]['host']
        port = config['servers'][server]['port']
        password = config['servers'][server]['password']
        players_online = 0

        try:
            mcr = MCRcon(host=host, password=password, port=port)
            mcr.connect()

            players_online = mcr.command('list')
            players_online = re.sub(r'(§[0-9]|§[a-z])', '', players_online)
            players_online = int(re.search(r'\d+|$', players_online).group())

            mcr.disconnect()
        except Exception as e:
            logging.warning(f'{host}:{port} could not be reached.\n{str(e)}')

        new_statuses[server] = players_online

    server_store = open(config['server_store'], 'r')
    cached_statuses = json.loads(server_store.read())
    server_store.close()

    if config['debug']:
        print('========= Loaded statuses =========')
        pprint(cached_statuses)
        print()

    for server in set(new_statuses.keys()).union(cached_statuses.keys()):
        if server in new_statuses and server in cached_statuses:
            cached_statuses[server].append(new_statuses[server])
            cached_statuses[server] = cached_statuses[server][-3:]

        elif server in new_statuses and server not in cached_statuses:
            logging.info(
                f'Found server {server} from config, adding to cached statuses list'
            )
            cached_statuses[server] = [new_statuses[server]]

        elif server not in new_statuses and server in cached_statuses:
            if len(cached_statuses[server]) == 3 and sum(
                    cached_statuses[server]) == 0:
                logging.warning(
                    f'Dropping check for server {server} due to inactivity')
                del cached_statuses[server]
            else:
                cached_statuses[server].append(0)
                cached_statuses[server] = cached_statuses[server][-3:]

        else:
            raise (Exception('Something\'s fucky'))

    server_store = open(config['server_store'], 'w')
    server_store.write(json.dumps(cached_statuses, indent=2))
    server_store.close()

    if config['debug']:
        print('======== Updated statuses =========')
        pprint(cached_statuses)
        print()