diff --git a/CHANGELOG.md b/CHANGELOG.md index d75ff5a8..06d8592f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ #Changelog# + +

1.0.19 rc 28

+Bugfixes: +- Bugfix commands.py line 784 where player message is used with no argument. +- fix player to player teleport ValueError bug caused by trying to read coordinates + where only a player name was given ("[lukeeexd: Teleported lukeeexd to Nyaii]") +- Fix Chat.py plugin (all player's were sharing the same chat configuration). +- Fix Chat.py breaking on /reload. +- bugfix message from offline wrapper that is a hub in mcserver.py. +- changed deop to allow any vanilla level 4 OP to run it. +- Explicitly `close()` sockets that were shutdown. +- substitute 'localhost' for code occurences of '127.0.0.1'. +New Plugin: +- add Geode plugin that prints each player's IP and country code at login. + +

1.0.17-18 rc 22-23

+-bugfix for non-proxy setups. See Dev changelogs (on development branch). +

1.0.15 rc 20

Primary reasons for update: - Regular update prior to further new Wrapper development diff --git a/Wrapper.py b/Wrapper.py index b391a46b..0fd85587 100644 Binary files a/Wrapper.py and b/Wrapper.py differ diff --git a/build/CHANGELOG.DEV.md b/build/CHANGELOG.DEV.md index 84f372f3..3e5b2038 100644 --- a/build/CHANGELOG.DEV.md +++ b/build/CHANGELOG.DEV.md @@ -1,4 +1,43 @@ +Build 28, 1.0.19 RC 28 +- Bugfix commands.py line 784 where player message is used with no argument. +- Update Master branch + +Build 27 +- increment version to 1.0.18 RC 27 +- fix player to player teleport ValueError bug caused by trying to read coordinates + where only a player name was given ("[lukeeexd: Teleported lukeeexd to Nyaii]") +- create PR to development. + +Build 26 +- Fix Chat.py plugin (all player's were sharing the same chat configuration). +- Fix Chat.py breaking on /reload. +- bugfix message from offline wrapper that is a hub in mcserver.py. +- changed deop to allow any vanilla level 4 OP to run it. +- add Geode plugin that prints each player's IP and country code at login. + +Build 25 +- Explicitly `close()` sockets that were shutdown. +- substitute 'localhost' for code occurences of '127.0.0.1'. + +Build 24 +Version number bumps to match Master 1.0.17 RC 24 + +Build 23 +- Bugfixes: + - at player logout (mcserver.py), server would attempt to run + proxy method removestaleclients(), even if proxy mode was not running. + - mcserver.py getplayer() not returning a player object in non-proxy mode. + +Build 22 +- Implement the proxy host as a ProcessPoolExecutor multiprocessor (only on Python3) + +Build 21 (In-process Dev build) [1.0.16 RC 21] +- Refractor proxy and remove external "ServerVitals" class and integrate wrapper into proxy again. + +Build 21 [1.0.16 RC 21] (Patch to Master) +- Bugfix - at player logout (mcserver.py), server would attempt to run + proxy method removestaleclients(), even if proxy mode was not running. Starting with: -Build 21 [1.0.15 RC 20] - Development branch update +Build 20 [1.0.15 RC 20] - Development branch update - includes first version of vanilla claims plugin. diff --git a/build/Wrapper.py.md5 b/build/Wrapper.py.md5 index bf9613f9..d8379bcc 100644 --- a/build/Wrapper.py.md5 +++ b/build/Wrapper.py.md5 @@ -1 +1 @@ -d5eda047085fd61c6a705dbeb4ca6ec7 \ No newline at end of file +d5aca2e96299c3d95a332fdf0b119b75 \ No newline at end of file diff --git a/build/buildinfo.py b/build/buildinfo.py index c6b6fc19..5d31059f 100644 --- a/build/buildinfo.py +++ b/build/buildinfo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- # Do not edit this file to bump versions; it is built automatically -__version__ = [1, 0, 15, 'rc', 20] +__version__ = [1, 0, 19, 'rc', 28] __branch__ = 'stable' diff --git a/build/version.json b/build/version.json index 45fa2fe6..1dc4bc97 100644 --- a/build/version.json +++ b/build/version.json @@ -4,11 +4,11 @@ "__version__": [ 1, 0, - 15, + 19, "rc", - 20 + 28 ], "build": 229, - "release_time": 1526743791.661702, + "release_time": 1534692383.768469, "repotype": "stable" } \ No newline at end of file diff --git a/docs/Wrapper.py.md5 b/docs/Wrapper.py.md5 index bf9613f9..d8379bcc 100644 --- a/docs/Wrapper.py.md5 +++ b/docs/Wrapper.py.md5 @@ -1 +1 @@ -d5eda047085fd61c6a705dbeb4ca6ec7 \ No newline at end of file +d5aca2e96299c3d95a332fdf0b119b75 \ No newline at end of file diff --git a/documentation/player.rst b/documentation/player.rst index d188d321..233a8993 100644 --- a/documentation/player.rst +++ b/documentation/player.rst @@ -85,7 +85,7 @@ .. -- connect(self, ip="127.0.0.1", port=25600) +- connect(self, ip="localhost", port=25600) Connect to another server. Upon calling, the client's current server instance will be closed and a new server connection made diff --git a/stable-plugins/Geode.py b/stable-plugins/Geode.py new file mode 100644 index 00000000..0477ff71 --- /dev/null +++ b/stable-plugins/Geode.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# pip install --user pygeoip +try: + import pygeoip +except ImportError: + pygeoip = False + DEPENDENCIES = ["ImportError: could not import package pygeoip", ] + +NAME = "Geode" +AUTHOR = "SurestTexas00" +ID = "com.suresttexas00.plugins.geode" +VERSION = (0, 1, 0) +SUMMARY = "Lookup player's country based on ip." +WEBSITE = "" +DESCRIPTION = """ +Test plugin to lookup player's country using pygeoip. +""" + + +# noinspection PyPep8Naming,PyMethodMayBeStatic,PyUnusedLocal +# noinspection PyClassicStyleClass,PyAttributeOutsideInit +class Main: + def __init__(self, api, log): + self.api = api + self.minecraft = api.minecraft + self.log = log + + def onEnable(self): + self.api.registerEvent("player.login", self.login) + self.data_storageobject = self.api.getStorage( + "geode", world=False, pickle=False + ) + self.data = self.data_storageobject.Data + self.gi = pygeoip.GeoIP('/usr/share/GeoIP/GeoIP.dat', + flags=pygeoip.const.MMAP_CACHE) + + def onDisable(self): + self.data_storageobject.close() + + def login(self, payload): + playerObj = payload["player"] + print(playerObj.ipaddress) + x = self.gi.country_code_by_addr(playerObj.ipaddress) + self.log.info( + "%s logged on from an ip (%s) in country '%s'." % ( + playerObj.username, playerObj.ipaddress, x) + ) diff --git a/stable-plugins/chat.py b/stable-plugins/chat.py index a95eeacd..7372d49a 100644 --- a/stable-plugins/chat.py +++ b/stable-plugins/chat.py @@ -1,5 +1,6 @@ # coding=utf-8 +import copy import sys PY3 = sys.version_info[0] > 2 @@ -21,25 +22,25 @@ SECT = u'\xa7' -# noinspection PyPep8Naming,PyCompatibility +# noinspection PyPep8Naming,PyMethodMayBeStatic,PyUnusedLocal +# noinspection PyClassicStyleClass,PyAttributeOutsideInit class Main: def __init__(self, api, log): + self.api = api self.log = log - self.ranks = {} - self.player_ranks = {} - self.default = { - "trans": False, - "isOp": False, - "rank": None, - "color": "f" # white - } def onEnable(self): self.api.registerEvent("player.chatbox", self.playerChatBox) - self.api.registerEvent("player.login", self.login) self.api.registerEvent("player.logout", self.logout) + self.player_ranks = {} + self.default = { + "trans": False, + "isOp": False, + "rank": None, + "color": "f" # white + } self.ranks = [ # from highest to lowest # perm display rank display color {"perm": "owner", "rank": "Owner", "color": "4"}, @@ -55,12 +56,11 @@ def onEnable(self): def onDisable(self): pass - def login(self, payload): - """ Obtain the player's rank when he logs in """ - player = payload["player"] - uuid = player.uuid - if uuid not in self.player_ranks: - self.player_ranks[uuid] = self.default + def verify(self, player): + """ Obtain the player's rank """ + if player.uuid not in self.player_ranks: + uuid = player.uuid + self.player_ranks[uuid] = copy.copy(self.default) if player.isOp(): self.player_ranks[uuid]["isOp"] = True @@ -85,8 +85,8 @@ def playerChatBox(self, payload): """ We can modify the chat. All the permissions legwork that could slow up the code - is already done just once at login.. we have to keep this - packet moving to prevent lag! + is done just once when player chat is first used.. + we have to keep this packet moving to prevent lag! {'translate': 'chat.type.text', 'with': [__[0]__{'clickEvent': {'action': 'suggest_command', @@ -103,6 +103,7 @@ def playerChatBox(self, payload): player = payload["player"] data = payload["json"] + self.verify(player) if "translate" in data: # special type of chat @@ -124,7 +125,7 @@ def playerChatBox(self, payload): # chatdisplayname[0] insertionplayer = self.api.minecraft.getPlayer(insertionplayername) # noqa - + self.verify(insertionplayer) # permanently tag OP suffix if self.player_ranks[insertionplayer.uuid]["isOp"]: data["with"][0]["text"] = "%s §8[OP]§r" % chatdisplayname diff --git a/stable-plugins/vanillaclaims.py b/stable-plugins/vanillaclaims.py new file mode 100644 index 00000000..1eeb385d --- /dev/null +++ b/stable-plugins/vanillaclaims.py @@ -0,0 +1,900 @@ +# -*- coding: utf-8 -*- + +import time +import threading + +NAME = "VanillaClaims" +AUTHOR = "SurestTexas00" +ID = "com.suresttexas00.vanillaclaims" +VERSION = (0, 9, 0) +SUMMARY = "Simple player-friendly land claim system" +WEBSITE = "" +DESCRIPTION = "Uses regions.py as the backend for protecting player claims." +DEPENDENCIES = ["regions.py", ] + +BEDROCK = 7 +GOLDBLOCK = 41 +DIAMONDBLOCK = 57 +LITREDSTONEORE = 74 + + +# The specific errors we need not worry about in the plugin API: +# noinspection PyMethodMayBeStatic,PyUnusedLocal +# noinspection PyPep8Naming,PyClassicStyleClass,PyAttributeOutsideInit +class Main: + def __init__(self, api, log): + self.api = api + self.log = log + + self.userperm = "vclaims.use" # change to None for anyone to access. + + # claim blocks configuration + self.defaultclaimblocks = 2048 # the starting number of blocks + self.actionsperblock = 50 # - sets the rate of block accumulation + self.maxclaimblocks = 200000 # - determine max earned claim blocks + # These prevent claims abuse or spurious annoyance-type claims + self.maxclaims = 5 # - max claims per player + self.minclaimsize = 81 # - minimum claim size + self.minclaimwidth = 6 # - minimum claim width + + # temporary run-time player data + self.player_dat = {} + + def onEnable(self): + # get the regions plugin: + self.regions = self.api.getPluginContext("com.suresttexas00.regions") + if self.regions.version < 1.3: + self.log.error( + "Regions.py is out of date!, Vanilla Claims not enabled." + ) + return False + + # init storage data + self.data_storageobject = self.api.getStorage( + "claimdata", world=False, pickle=False) + self.data = self.data_storageobject.Data + + self.api.registerEvent("player.place", self.action_place) + self.api.registerEvent("player.spawned", self.playerSpawned) + self.api.registerEvent("player.dig", self.action_dig) + + user = self.userperm + admin = "vclaims.admin" + + self.api.registerHelp( + "VanillaClaims", "Claim based grief prevention system.", [ + ("/abandonclaim", + "Abandon the claim you are standing in.", user), + ("/abandonallclaims", + "Abandon all claims you own.", user), + ("/claimblocks", + "View your claims blocks information.", user), + ("/trust ", + "Allow player full access to your claim.", user), + ("/breaktrust ", + "Allow player to break blocks on your claim.", user), + ("/placetrust ", + "Allow player to place blocks / operate things.", user), + ("/accesstrust ", + "Allow player interaction (place lava, eat, throw pearls, etc)", # noqa + user), + ("/untrust ", + "remove a player from your claim.", user), + ("/trustlist", + "Display the claim's player information.", user), + ("/show", + "Show the claim you are standing in.", user), + ("/spade", + "Gives the land claim gold shovel", user), + ("/adjustclaimblocks ", + "Adjust player claim blocks.", admin), + ("/transferclaim ", + "re-creates the same claim in another's name", + admin), + ("/deleteclaim", + "Delete the claim you are standing in", admin), + ("/deleteallclaims ", + "Delete all a players claims.", admin) + ]) + + self.api.registerCommand( + "show", self._show_comm, user) + self.api.registerCommand( + ("spade", "wand", "shovel"), self._spade, user) + self.api.registerCommand( + ("abandonclaim", "abandonclaims"), self._abandonclaim_comm, user) + self.api.registerCommand( + "abandonallclaims", self._abandonallclaims_comm, user) + self.api.registerCommand( + "deleteclaim", self._deleteclaim_comm, admin) + self.api.registerCommand( + "deleteallclaims", self._deleteallclaims_comm, admin) + self.api.registerCommand( + "adjustclaimblocks", self._adjustclaimblocks_comm, admin) + self.api.registerCommand( + "transferclaim", self._transferclaim_comm, admin) + self.api.registerCommand( + ("claimblocks", "claimblock", "claimsblock", "claimsblocks"), + self._claimblocks_comm, user) + self.api.registerCommand( + "trust", self._trust_comm, user) + self.api.registerCommand( + "breaktrust", self._breaktrust_comm, user) + self.api.registerCommand( + "placetrust", self._placetrust_comm, user) + self.api.registerCommand( + "accesstrust", self._accesstrust_comm, user) + self.api.registerCommand( + "untrust", self._untrust_comm, user) + # self.api.registerCommand("trustlist", self._trustlist_comm, user) + + self.run = True + self.action_queue = [] + tr = threading.Thread(target=self._track_activity, + name="trackblocks", args=()) + tr.daemon = True + tr.start() + + def onDisable(self): + self.run = False + self.data_storageobject.close() + + def get_claim_data(self, playerobj): + """ + Get the player's claim data. + + :param playerobj: + :returns: A dictionary of player claim data. + + """ + try: + return self.data[playerobj.uuid] + except KeyError: + self.data[playerobj.uuid] = { + "blocks": self.defaultclaimblocks, + "blocksused": 0, + "activity": 0, + "playername": playerobj.username, + "claimlist": [], + "claiminfo": {}, + "laston": time.time(), + "admin": playerobj.hasPermission("vclaims.admin") + } + return self.data[playerobj.uuid] + + def get_player_data(self, name): + """ + Get the player's selection data. Selection data is not saved + between reboots. + + :param name: player name. + + :returns: A dictionary of player selection data. + { + "point1" + "point2" + "dim1" + "dim2" + "mode" # selection modes ... + "point" # which point is being selected + "proposed" # cubic tuple (a set of hi/low coords) + "rgedit" name of region being edited/resized + } + """ + try: + return self.player_dat[name] + except KeyError: + self.player_dat[name] = {} + self._finishwithselectionmode(name) + return self.player_dat[name] + + def _track_activity(self): + """ + Each item is a playeruuid, player object entry. + """ + + while self.run: + time.sleep(.4) + self._on_timer() + time.sleep(.4) + self._on_timer() + while len(self.action_queue) > 0: + # grab next change + player = self.action_queue.pop(0) + p = self.get_claim_data(player) + # block increaser + p["activity"] += 1 + activities = p["activity"] + if activities % self.actionsperblock is not 0: + continue + p["activity"] = 0 + if p["blocks"] < self.maxclaimblocks: + p["blocks"] += 1 + + def _on_timer(self): + """ + Puts each player in or out of claims mode. + Modes: + None: has no shovel + Idle: has shovel, no action taken yet + + """ + while self.run: + time.sleep(.5) + for players in self.api.minecraft.getPlayers(): + player = self.api.minecraft.getPlayer(players) + try: + itemid = player.getHeldItem()["id"] + except AttributeError: + # probably a bad player object + continue + + p = self.get_player_data(player.username) + + # "mode" = selection mode + if itemid == 284 and p["mode"] is None: + player.message( + "&2Claims shovel active... Click an area to edit " + "or to claim." + ) + p["mode"] = "idle" + + if itemid != 284 and p["mode"]: + player.message( + "&2Shovel put away... Switching out of claims " + "mode" + ) + self._finishwithselectionmode(player.username) + + def playerSpawned(self, payload): + try: + player = payload["player"] + except AttributeError: + self.log.error("VanillaClaims player spawn not successful. " + "Payload: %s" % payload) + return + cl = self.get_claim_data(player) + cl["laston"] = time.time() + cl["admin"] = player.hasPermission("vclaims.admin") + + def action_dig(self, payload): + try: + player = payload["player"] + action = payload["action"] + itemid = player.getHeldItem()["id"] + except AttributeError: + # probably a bad player object + return False + if itemid == 284: + if action == "begin_break": + try: + position = payload["position"] + dim = player.getDimension() + except AttributeError: + return False + self.wand_use(player, player.uuid, position, dim) + return False + + # update activity + if action == "end_break": + self.action_queue.append(player) + + def action_place(self, payload): + try: + player = payload["player"] + itemid = player.getHeldItem()["id"] + except AttributeError: + return False + + if itemid == 284: + try: + dim = int(player.getDimension()) + clickposition = payload["clickposition"] + except AttributeError: + return False + self.wand_use(player, player.uuid, clickposition, dim) + # never allow gold shovel use - reserved for claims + return False + self.action_queue.append(player) + + def wand_use(self, player, playeruuid, position, dim): + """ + + :param player: + :param playeruuid: + :param position: + :param dim: + :return: + """ + + p = self.get_player_data(player.username) + cl = self.get_claim_data(player) + + # point = self.data[playeruuid]["point"] + # mode = p["mode"] + anyregion = self.regions.regionname(position, dim) + if anyregion: + region_owner_uuid = self.regions.getregioninfo( + anyregion, "ownerUuid" + ) + else: + region_owner_uuid = False + + # find the selection mode + # first click point1 determines the selection mode + if p["point"] == "point1": + # If not your claim, draw it and go back to idle mode + if region_owner_uuid and region_owner_uuid != playeruuid: + # TODO draw the claim you are in, message with "not your claim" + otheruuid = self.regions.getregioninfo( + anyregion, "ownerUuid" + ) + othername = self.api.minecraft.lookupbyUUID(otheruuid) + player.message("&cThis area is claimed by %s..." % othername) + point1 = self.regions.getregioninfo( + anyregion, "pos1" + ) + point2 = self.regions.getregioninfo( + anyregion, "pos2" + ) + x = int(position[0]) + y = int(position[1]) - 1 + z = int(position[2]) + self._show(player, (x, y, z), point1, point2) + self._reset_selections(playeruuid) + p["mode"] = "idle" + return + # if unclaimed, go to 'new' mode + elif not anyregion and p["mode"] == "idle": + p["mode"] = "new" + return + # If your claim, then go to 'edit' mode (but don't make first point) + elif region_owner_uuid == playeruuid and p["mode"] == "idle": + p["mode"] = "edit" + p["rgedit"] = anyregion + # TODO STUFF to draw claim, place edit markers + player.sendBlock(cl["claiminfo"][anyregion]["handle1"], + LITREDSTONEORE, 0) + player.sendBlock(cl["claiminfo"][anyregion]["handle2"], + LITREDSTONEORE, 0) + player.message( + { + "text": + "You are now editing the claim marked by the " + "restone corners. Select a new corner (to cancel, " + "put shovel away).", + "color": "gold" + } + ) + return + else: + # -we know it is not claimed (or is owned by player) + # only successful selection as point1 enables point2 + p["point1"] = position + p["dim1"] = dim + p["point"] = "point2" + # re-draw new point1 as point1 color (diamond) + player.sendBlock(p["point1"], DIAMONDBLOCK, 0) + return + elif p["point"] == "point2": + # If not your claim, draw the conflicting claim + if region_owner_uuid and region_owner_uuid != playeruuid: + # TODO draw the claim you are in, message with "not your claim" + return + p["point2"] = position + p["dim2"] = dim + # Stay at point two until successful (unless user cycles wand) + p["point"] = "point2" + # UNLESS, they re-select point1: + if p["point2"] == p["point1"]: + p["point"] = "point1" + # TODO tell player to continue with selecting first point. + return + # or dimensions don't match: + if p["dim1"] != p["dim2"]: + self._reset_selections(playeruuid) + p["mode"] = "idle" + return + # We now have a 'tentative' claim we can validate: + # We know it's corner points were not claimed... but: + p["proposed"] = self.regions.stat_normalize_selection( + p["point1"], p["point2"] + ) + newclaim = self._claim(player) + if newclaim: + # player.sendBlock(position, GOLDBLOCK, 0) + player.message("&6Second Corner selected.") + player.message("&6Claim action successful.") + + pos1 = cl["claiminfo"][newclaim]["handle1"] + pos2 = cl["claiminfo"][newclaim]["handle2"] + + player.sendBlock(pos1, DIAMONDBLOCK, 0) + player.sendBlock(pos2, GOLDBLOCK, 0) + + low = self.regions.getregioninfo(newclaim, "pos1") + high = self.regions.getregioninfo(newclaim, "pos2") + x = int(pos2[0]) + y = int(pos2[1]) - 1 + z = int(pos2[2]) + self._show(player, (x, y, z), low, high) + p["mode"] = None + p["point"] = "point1" + return + if newclaim is False: + player.message("&cClaim action failed.") + player.message( + { + "text": "Change or edit selection area by selecting" + " a restone corner and then selecting a " + "new spot", "color": "yellow" + } + ) + player.sendBlock( + p["point1"], + LITREDSTONEORE, 0 + ) + player.sendBlock( + p["point2"], + LITREDSTONEORE, 0 + ) + self.data[playeruuid]["point"] = "error" + return + + def _claim(self, player): + playerid = player.uuid + p = self.get_player_data(player.username) + cl = self.get_claim_data(player) + # 1) calculate the claim size parameters + lowcoord = p["proposed"][0] + highcoord = p["proposed"][1] + blocks, length, width = self._getsizeclaim(lowcoord, highcoord) + playersblocks = cl["blocks"] + usedblocks = cl["blocksused"] + if p["mode"] == "edit": + # credit back blocks used by an existing claim being edited. + pos1 = self.regions.getregioninfo(p["rgedit"], "pos1") + pos2 = self.regions.getregioninfo(p["rgedit"], "pos2") + x_dia = pos2[0] - pos1[0] + z_dia = pos2[2] - pos1[2] + usedblocks -= (x_dia * z_dia) + + if blocks < self.minclaimsize: + player.message( + "&cClaim (%d) is not minimum size of at least %d blocks." % ( + blocks, self.minclaimsize + ) + ) + return False + + if length < self.minclaimwidth or width < self.minclaimwidth: + player.message( + "&cClaim has to be at least %d blocks wide all " + "around." % self.minclaimwidth + ) + return False + + if blocks > playersblocks - usedblocks: + player.message( + "&cYou do not have enough blocks to make this claim." + ) + player.message("&eYou have: %d" % playersblocks) + player.message("&eYou need: %d" % blocks) + return False + + # 2) look for intersecting regions + collisions = self.regions.intersecting_regions( + p["dim1"], lowcoord, highcoord, rect=True + ) + if collisions: + if p["mode"] == "edit" and len( + collisions) < 2 and p["rgedit"] in collisions: + collisions.remove(p["rgedit"]) + if len(collisions) > 0: + player.message("&cArea overlaps region(s):") + player.message("&5%s" % str(collisions).replace("u'", "'")) + return False + + if p["mode"] == "edit": + self._delete_claim(player, p["rgedit"], playerid) + + # get a new claims region name + thisclaimname, humanname = self._get_claimname( + playerid, cl["admin"] + ) + if not thisclaimname: + player.message( + "&cyou have claimed the maximum number of areas " + "(%s)" % self.maxclaims + ) + return False + # 3) inspections passed, claim allowed. + lowcorner = (lowcoord[0], 5, lowcoord[2]) + highcorner = (highcoord[0], 255, highcoord[2]) + handle1 = p["point1"] + handle2 = p["point2"] + self.regions.rgdefine( + thisclaimname, playerid, p["dim1"], lowcorner, highcorner + ) + self.regions.protection_on(thisclaimname) + player.message( + "&2Region &5%s&2 created and protected." % thisclaimname + ) + + cl["claimlist"].append(thisclaimname) + cl["claiminfo"][thisclaimname] = {} + cl["claiminfo"][thisclaimname]["handle1"] = handle1 + cl["claiminfo"][thisclaimname]["handle2"] = handle2 + cl["blocksused"] += blocks + + self._reset_selections(player.username) + + p["mode"] = None + return thisclaimname + + def _check_username(self, playerobj, args, usage_msg, arg_count=1): + if len(args) < arg_count: + playerobj.message("&cUsage: %s" % usage_msg) + return False + targetuuid = str(self.api.minecraft.lookupbyName(args[0])) + targetname = self.api.minecraft.lookupbyUUID(targetuuid) + if not targetuuid or targetname.lower() != args[0].lower(): + playerobj.message("&cinvalid username.") + return False + return targetname, targetuuid + + def _check_regionname(self, player): + try: + position = player.getPosition() + dim = player.getDimension() + except AttributeError: + return False + regionname = self.regions.regionname(position, dim) + if regionname: + return regionname, position, dim + else: + player.message("&cYou are not standing in a claim.") + return False + + def _trust_comm(self, *args): + player = args[0] + try: + targetname, targetuuid = self._check_username( + player, args[1], "&/trust ") + regionname, position, dim = self._check_regionname(player) + except TypeError: + return + + owner = self.regions.getregioninfo(regionname, "ownerUuid") + playeruuid = str(player.mojangUuid) + if playeruuid != owner: + player.message("&cThis is not your claim.") + return + self.regions.rgedit( + regionname, playername=targetname, + addbreak=True, addplace=True, addaccess=True + ) + player.message( + "&e%s has been granted full access to this claim." % targetname + ) + + def _untrust_comm(self, *args): + player = args[0] + try: + targetname, targetuuid = self._check_username( + player, args[1], "/untrust ") + regionname, position, dim = self._check_regionname(player) + except TypeError: + return + + owner = self.regions.getregioninfo(regionname, "ownerUuid") + playeruuid = str(player.mojangUuid) + if playeruuid != owner: + player.message("&cThis is not your claim.") + return + self.regions.rgedit( + regionname, playername=targetname, remove=True + ) + player.message("&e%s has been removed from this claim." % targetname) + + def _breaktrust_comm(self, *args): + player = args[0] + try: + targetname, targetuuid = self._check_username( + player, args[1], "/breaktrust ") + regionname, position, dim = self._check_regionname(player) + except TypeError: + return + + owner = self.regions.getregioninfo(regionname, "ownerUuid") + playeruuid = str(player.mojangUuid) + if playeruuid != owner: + player.message("&cThis is not your claim.") + return + self.regions.rgedit( + regionname, playername=targetname, addbreak=True + ) + player.message( + "&e%s added to this claim. Player can now break items " + "here." % targetname + ) + + def _placetrust_comm(self, *args): + player = args[0] + try: + targetname, targetuuid = self._check_username( + player, args[1], "/placetrust ") + regionname, position, dim = self._check_regionname(player) + except TypeError: + return + + owner = self.regions.getregioninfo(regionname, "ownerUuid") + playeruuid = str(player.mojangUuid) + if playeruuid != owner: + player.message("&cThis is not your claim.") + return + self.regions.rgedit( + regionname, playername=targetname, addplace=True + ) + player.message( + "&e%s added to this claim. Player can now access/place items" + " here." % targetname) + + def _accesstrust_comm(self, *args): + player = args[0] + try: + targetname, targetuuid = self._check_username( + player, args[1], "/accesstrust ") + regionname, position, dim = self._check_regionname(player) + except TypeError: + return + + owner = self.regions.getregioninfo(regionname, "ownerUuid") + playeruuid = str(player.mojangUuid) + if playeruuid != owner: + player.message("&cThis is not your claim.") + return + self.regions.rgedit( + regionname, playername=targetname, addaccess=True + ) + player.message( + "&e%s added to this claim. Player can now access/place items" + " here." % targetname + ) + + def _claimblocks_comm(self, *args): + player = args[0] + self._claimblocks(player, player.uuid, player.username) + + def _adjustclaimblocks_comm(self, player, args): + player = args[0] + try: + targetname, targetuuid = self._check_username( + player, args[1], + "/adjustclaimblocks ", 3 + ) + except TypeError: + return + + amount = int(args[2]) + subcommmand = str(args[1]).lower() + blocksinuse = self.data[targetuuid]["blocksused"] + if subcommmand == "add": + self.data[targetuuid]["blocks"] += amount + player.message( + "&eAdded %s blocks to player %s" % (amount, targetname) + ) + if subcommmand == "sub": + self.data[targetuuid]["blocks"] -= amount + player.message( + "&eremoved %s blocks from player %s" % (amount, targetname) + ) + if subcommmand == "set": + self.data[targetuuid]["blocks"] = amount + player.message("&eset %s's blocks to %s" % (targetname, amount)) + if subcommmand == "info": + self._claimblocks(player, targetuuid, targetname) + return + amount = self.data[targetuuid]["blocks"] + player.message("&2%s has %s blocks." % (targetname, amount)) + + def _spade(self, *args): + player = args[0] + self.api.minecraft.console( + "give %s minecraft:golden_shovel 1 32" % player.username + ) + + def _abandonclaim_comm(self, *args): + player = args[0] + try: + regionname, position, dim = self._check_regionname(player) + except TypeError: + return + + owner = self.regions.getregioninfo(regionname, "ownerUuid") + if player.uuid != owner: + player.message("&cThis is not your claim.") + return + self._abandonclaim(player, regionname, player.uuid) + + def _deleteclaim_comm(self, *args): + player = args[0] + try: + regionname, position, dim = self._check_regionname(player) + except TypeError: + return + owneruuid = self.regions.getregioninfo(regionname, "ownerUuid") + self._abandonclaim(player, regionname, owneruuid) + + def _abandonallclaims_comm(self, *args): + player = args[0] + playerid = str(player.mojangUuid) + claimlist = list(self.data[playerid]["claimlist"]) + for thisclaimname in claimlist: + self._abandonclaim(player, str(thisclaimname), playerid) + return + + def _deleteallclaims_comm(self, *args): + player = args[0] + try: + targetname, targetuuid = self._check_username( + player, args[1], "/deleteallclaims ", + ) + except TypeError: + return + + claimlist = list(self.data[targetuuid]["claimlist"]) + for thisclaimname in claimlist: + self._abandonclaim(player, str(thisclaimname), targetuuid) + return + + def _transferclaim_comm(self, *args): + player = args[0] + try: + regionname, position, dim = self._check_regionname(player) + targetname, targetuuid = self._check_username( + player, args[1], "/transferclaim " + ) + except TypeError: + return + + oldowneruuid = self.regions.getregioninfo(regionname, "ownerUuid") + pos1 = self.regions.getregioninfo(regionname, "pos1") + pos2 = self.regions.getregioninfo(regionname, "pos2") + if targetuuid not in self.data: + self._init_player_record(targetuuid, targetname) + self._abandonclaim(player, regionname, oldowneruuid) + self._quick_claim( + player, targetuuid, dim, pos1, pos2, ycoords_of_feet=position[1] + ) + + def _show_comm(self, *args): + player = args[0] + pos5 = player.getPosition() + pos = (int(pos5[0]), int(pos5[1]), int(pos5[2])) + dim = player.getDimension() + regionname = self.regions.regionname(pos, dim) + if regionname is False: + player.message("ðis is unclaimed") + return + point1 = self.regions.getregioninfo( + regionname, "pos1" + ) + point2 = self.regions.getregioninfo( + regionname, "pos2" + ) + x = int(pos[0]) + y = int(pos[1]) - 1 + z = int(pos[2]) + self._show(player, (x, y, z), point1, point2) + player.message("&eRegion: &5%s" % regionname) + + def _claimblocks(self, playerobject, playerid, playername): + totalblocks = self.data[playerid]["blocks"] + blocksinuse = self.data[playerid]["blocksused"] + blocksavailable = totalblocks - blocksinuse + + claimlist = self.data[playerid]["claimlist"] + totalclaims = len(claimlist) + playerobject.message("") + playerobject.message("&6Claims information for &e%s&6:" % playername) + playerobject.message("&6Total claim blocks &5%s&6" % totalblocks) + playerobject.message( + "&6Blocks used: &5%s &6Blocks available: &5%s" % ( + blocksinuse, blocksavailable + ) + ) + playerobject.message("&6Using &5%s&6 claims." % totalclaims) + + def _delete_claim(self, player, claimname, ownerid): + cl = self.get_claim_data(player) + handle1 = cl["claiminfo"][claimname]["handle1"] + handle2 = cl["claiminfo"][claimname]["handle2"] + blocksize, length, width = self._getsizeclaim(handle1, handle2) + if claimname in cl["claiminfo"]: + del cl["claiminfo"][claimname] + if claimname in cl["claimlist"]: + cl["claimlist"].remove(claimname) + self.regions.clicks_queue.append(["break", player, handle1]) + self.regions.clicks_queue.append(["break", player, handle2]) + self.regions.rgdelete(claimname) + cl["blocksused"] -= blocksize + + def _abandonclaim(self, player, thisclaimname, playerid): + self._delete_claim(player, thisclaimname, playerid) + player.message("&eClaim %s deleted!" % thisclaimname) + + def _get_claimname(self, playerid, admin): + x = 0 + humanname = self.data[playerid]["playername"] + + while True: + claimname = "%s-%d" % (humanname, x) + if claimname not in self.data[playerid]["claimlist"]: + break + else: + x += 1 + if x > self.maxclaims and not admin: + return False, False + return claimname, humanname + + def _show(self, player, position, low, high): + """ + + :param player: + :param position: + :param low: + :param high: + :return: + """ + lowcorner = low[0], position[1] + 1, low[2] + highcorner = (high[0], position[1] + 4, high[2]) + self.regions.client_show_cube( + player, lowcorner, highcorner, sendblock=False + ) + + def _finishwithselectionmode(self, playername): + self._reset_selections(playername) + p = self.player_dat[playername] + p["mode"] = None + + def _reset_selections(self, playername): + p = self.player_dat[playername] + p["point1"] = [0, 0, 0] + p["point2"] = [0, 0, 0] + p["dim1"] = 0 + p["dim2"] = 0 + p["point"] = "point1" + p["proposed"] = [0, 0, 0], [0, 0, 0] + p["rgedit"] = None + + def _quick_claim(self, player_msg_object, owneruuid, + dim, pos1, pos2, ycoords_of_feet=62): + data = self.data[owneruuid] + + data["point"] = "point2" + data["dim1"] = dim + data["dim2"] = dim + data["point1"] = pos1 + data["point2"] = pos2 + low, high = self.regions.stat_normalize_selection( + data["point1"], + data["point2"] + ) + highcorner = high[0], ycoords_of_feet + 1, high[2] + lowcorner = low[0], ycoords_of_feet + 5, low[2] + data["point3"] = lowcorner + data["point4"] = highcorner + self.data_storageobject.save() + claimed = self._claim(player_msg_object, owneruuid) + if claimed: + player_msg_object.message("&6Claim action successful.") + data["mode"] = "none" + + @staticmethod + def _getsizeclaim(coord1, coord2): + # size is including boundary + width = abs(coord2[0] - coord1[0]) + 1 + length = abs(coord2[2] - coord1[2]) + 1 + blocks = length * width + return blocks, length, width diff --git a/wrapper/api/minecraft.py b/wrapper/api/minecraft.py index d8a48a69..e898d991 100644 --- a/wrapper/api/minecraft.py +++ b/wrapper/api/minecraft.py @@ -47,7 +47,7 @@ def isServerStarted(self): Return a boolean indicating if the server is fully booted or not. """ - return self.wrapper.servervitals.state == 2 + return self.wrapper.javaserver.state == 2 def changeServerProps(self, config_item, new_value, reload_server=False): """ @@ -147,7 +147,7 @@ def getServerPackets(self, packetset="CB"): if not self.wrapper.proxymode: return False - version = self.wrapper.proxy.srv_data.protocolVersion + version = self.wrapper.javaserver.protocolVersion if packetset == "SB": return ServerBound(version) @@ -170,7 +170,7 @@ def getTimeofDay(self, dttmformat=0): # 0 = ticks, 1 = Military, else = civilian AM/PM, return -1 if no one # on or server not started if self.isServerStarted is True and self.wrapper.proxymode: - servertimeofday = self.wrapper.proxy.srv_data.timeofday + servertimeofday = self.wrapper.javaserver.timeofday ticktime = servertimeofday % 24000 if dttmformat == 0 or ticktime == -1: return ticktime @@ -282,7 +282,7 @@ def getPlayers(self): # returns a list of players Returns a list of the currently connected players. """ - return self.wrapper.servervitals.players + return self.wrapper.players def getEntityControl(self): """ @@ -342,7 +342,7 @@ def getPlayer(self, username=""): """ try: - return self.wrapper.servervitals.players[str(username)] + return self.wrapper.players[str(username)] except Exception as e: self.log.error("No such player %s is logged in:\n%s", username, e) @@ -356,7 +356,7 @@ def getplayerby_eid(self, eid): :returns: The Player Class object for the specified EID. If the EID is not a player or is not found, returns False """ - for client in self.wrapper.servervitals.clients: + for client in self.wrapper.proxy.clients: if client.server_eid == eid: try: return self.wrapper.players[client.username] @@ -705,7 +705,7 @@ def getLevelInfo(self, worldname=False): """ if not worldname: - worldname = self.wrapper.servervitals.worldname + worldname = self.wrapper.javaserver.worldname if not worldname: raise Exception("Server Uninitiated") f = NBTFile("%s/%s/level.dat" % (self.serverpath, worldname), "rb") @@ -768,7 +768,7 @@ def getServerPath(self): Gets the server's path. """ - return self.wrapper.servervitals.serverpath + return self.wrapper.javaserver.serverpath def getWorld(self): """ @@ -787,7 +787,7 @@ def getWorldName(self): new server instance not started, it will return the old world name. """ - return self.wrapper.servervitals.worldname + return self.wrapper.javaserver.worldname def getUuidCache(self): """ diff --git a/wrapper/api/player.py b/wrapper/api/player.py index 723752e8..a288af50 100644 --- a/wrapper/api/player.py +++ b/wrapper/api/player.py @@ -185,7 +185,7 @@ def __init__(self, username, wrapper): self._position = [0, 0, 0, 0, 0] # internally used for non-proxy mode self.client = None - self.clientgameversion = self.wrapper.servervitals.protocolVersion + self.clientgameversion = self.wrapper.javaserver.protocolVersion self.cbpkt = Packets_cb(self.clientgameversion) self.sbpkt = Packets_sb(self.clientgameversion) @@ -204,7 +204,7 @@ def __init__(self, username, wrapper): if self.wrapper.proxy: gotclient = False - for client in self.wrapper.servervitals.clients: + for client in self.wrapper.proxy.clients: if client.username == self.username: self.client = client self.clientUuid = client.wrapper_uuid @@ -212,13 +212,13 @@ def __init__(self, username, wrapper): self.mojangUuid = client.mojanguuid self.ipaddress = client.ip - # pktSB already set to self.wrapper.servervitals.protocolVersion # noqa + # pktSB already set to self.wrapper.javaserver.protocolVersion # noqa self.clientboundPackets = self.client.pktCB self.clientgameversion = self.client.clientversion gotclient = True break if not gotclient: - pprint.pprint(self.wrapper.servervitals.clients) + pprint.pprint(self.wrapper.proxy.clients) self.log.error("Proxy is on, but this client is not " "listed in proxy.clients!") self.log.error("The usual cause of this would be that" @@ -325,12 +325,12 @@ def execute(self, string): "execute" command. """ - if string[0] in (self.wrapper.servervitals.command_prefix, "/"): + if string[0] in (self.wrapper.proxy.command_prefix, "/"): string = string[1:] try: self.client.chat_to_server("/%s" % string) except AttributeError: - if self.wrapper.servervitals.protocolVersion > PROTOCOL_1_7_9: + if self.wrapper.javaserver.protocolVersion > PROTOCOL_1_7_9: self.wrapper.javaserver.console( "execute %s ~ ~ ~ %s" % (self.username, string)) else: @@ -398,7 +398,7 @@ def getClient(self): """ if self.client is None: - for client in self.wrapper.servervitals.clients: + for client in self.wrapper.proxy.clients: if client.username == self.username: self.client = client return client @@ -506,7 +506,7 @@ def setResourcePack(self, url, hashrp=""): """ try: - version = self.wrapper.proxy.srv_data.protocolVersion + version = self.wrapper.javaserver.protocolVersion except AttributeError: # Non proxy mode return False @@ -550,10 +550,10 @@ def isOp(self, strict=False): """ - if self.wrapper.servervitals.operator_list in (False, None): + if self.wrapper.javaserver.operator_list in (False, None): return False # no ops in file # each op item is a dictionary - for ops in self.wrapper.servervitals.operator_list: + for ops in self.wrapper.javaserver.operator_list: if ops["uuid"] == self.serverUuid.string: return ops["level"] if ops["name"] == self.username and not strict: @@ -603,7 +603,7 @@ def setVisualXP(self, progress, level, total): """ try: - version = self.wrapper.proxy.srv_data.protocolVersion + version = self.wrapper.javaserver.protocolVersion except AttributeError: # Non proxy mode return False @@ -662,7 +662,7 @@ def openWindow(self, windowtype, title, slots): """ try: - version = self.wrapper.proxy.srv_data.protocolVersion + version = self.wrapper.javaserver.protocolVersion except AttributeError: # Non proxy mode return False @@ -1077,7 +1077,7 @@ def getFirstLogin(self): return self.data.Data["firstLoggedIn"] # Cross-server commands - def connect(self, ip="127.0.0.1", port=25600): + def connect(self, ip="localhost", port=25600): """ Connect to another server. Upon calling, the client's current server instance will be closed and a new server connection made diff --git a/wrapper/api/world.py b/wrapper/api/world.py index fae97200..9f0d66a0 100644 --- a/wrapper/api/world.py +++ b/wrapper/api/world.py @@ -64,7 +64,7 @@ def fill(self, position1, position2, tilename, damage=0, mode="destroy", data=No x1, y1, z1 = position1 x2, y2, z2 = position2 - if self.javaserver.vitals.protocolVersion < 6: + if self.javaserver.protocolVersion < 6: raise Exception("Must be running Minecraft 1.8 or above" " to use the world.fill() method.") else: @@ -82,7 +82,7 @@ def replace(self, position1, position2, tilename1, damage1, tilename2, damage2=0 """ x1, y1, z1 = position1 x2, y2, z2 = position2 - if self.javaserver.vitals.protocolVersion < 6: + if self.javaserver.protocolVersion < 6: raise Exception( "Must be running Minecraft 1.8 or above" " to use the world.replace() method.") diff --git a/wrapper/core/backups.py b/wrapper/core/backups.py index 62b0f260..181078fc 100644 --- a/wrapper/core/backups.py +++ b/wrapper/core/backups.py @@ -63,7 +63,7 @@ def _bu_process(self): while not self.wrapper.haltsig.halt: time.sleep(1) # only run backups in server running/starting states - if self.wrapper.javaserver.vitals.state in (1, 2) and not self.idle: + if self.wrapper.javaserver.state in (1, 2) and not self.idle: if time.time() - self.timer > self.backup_interval and self.enabled: # noqa self.dobackup() else: diff --git a/wrapper/core/buildinfo.py b/wrapper/core/buildinfo.py index c6b6fc19..5d31059f 100644 --- a/wrapper/core/buildinfo.py +++ b/wrapper/core/buildinfo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- # Do not edit this file to bump versions; it is built automatically -__version__ = [1, 0, 15, 'rc', 20] +__version__ = [1, 0, 19, 'rc', 28] __branch__ = 'stable' diff --git a/wrapper/core/commands.py b/wrapper/core/commands.py index 5c6f48f2..883f12f7 100644 --- a/wrapper/core/commands.py +++ b/wrapper/core/commands.py @@ -521,7 +521,7 @@ def command_help(self, player, payload): "clickEvent": { "action": "run_command", "value": "%shelp Minecraft %d" % ( - self.wrapper.servervitals.command_prefix, + self.wrapper.proxy.command_prefix, page + 2) } }, { @@ -579,7 +579,7 @@ def command_help(self, player, payload): "clickEvent": { "action": "suggest_command", "value": "%s%s" % ( - self.wrapper.servervitals.command_prefix, # noqa + self.wrapper.proxy.command_prefix, # noqa command[1:]) }, "hoverEvent": { @@ -607,7 +607,7 @@ def command_help(self, player, payload): _showpage( player, page, items, "help %s" % groupname, 4, - command_prefix=self.wrapper.servervitals.command_prefix) # noqa + command_prefix=self.wrapper.proxy.command_prefix) # noqa return player.message("&cThe help group '%s' does not exist." % group) @@ -638,13 +638,13 @@ def command_help(self, player, payload): "clickEvent": { "action": "run_command", "value": "%shelp %s" % ( - self.wrapper.servervitals.command_prefix, + self.wrapper.proxy.command_prefix, v["name"]) }, "hoverEvent": { "action": "show_text", "value": "%shelp %s" % ( - self.wrapper.servervitals.command_prefix, + self.wrapper.proxy.command_prefix, v["name"]) } }, @@ -653,7 +653,7 @@ def command_help(self, player, payload): }] }) _showpage(player, page, items, "help", 8, - command_prefix=self.wrapper.servervitals.command_prefix) + command_prefix=self.wrapper.proxy.command_prefix) return False def command_password(self, player, payload): @@ -781,7 +781,7 @@ def _command_whitelist_add(self, player, arg): if not self.wrapper.proxymode: player.execute("whitelist add %s" % arg) player.message("..Working. Server may lag.") - player.message() + player.message("...") return whitelist = getjsonfile( "whitelist", self.wrapper.serverpath, self.wrapper.encoding @@ -908,18 +908,17 @@ def _command_whitelist_online(self, player, _arg): self.wrapper.shutdown() def command_deop(self, player, payload): - # if player is None: - # player = self.wrapper.xplayer - if not self._superop(player, 9): + """DeOP has lower permission level""" + if not self._superop(player, 3): return False operator_name = getargs(payload["args"], 0) - if self.wrapper.servervitals.state == 2: + if self.wrapper.javaserver.state == 2: # deop from server self.wrapper.javaserver.console("deop %s" % operator_name) # deop from superops.txt file_text = "" - owner_names = self.wrapper.servervitals.ownernames + owner_names = self.wrapper.javaserver.ownernames for eachname in owner_names: if eachname != operator_name: if eachname not in ("", ""): @@ -977,13 +976,13 @@ def command_op(self, player, payload): if superop and superlevel > 4: superlevel = max(5, superlevel) # 2 = make sure server STARTED - if self.wrapper.servervitals.state == 2: + if self.wrapper.javaserver.state == 2: self.wrapper.javaserver.console("op %s" % name) # if not, wrapper makes ops.json edits else: self.wrapper.javaserver.refresh_ops(read_super_ops=False) - oplist = self.wrapper.servervitals.operator_list + oplist = self.wrapper.javaserver.operator_list newop_item = { "uuid": uuid, "name": name, diff --git a/wrapper/core/events.py b/wrapper/core/events.py index ea91d094..89923106 100644 --- a/wrapper/core/events.py +++ b/wrapper/core/events.py @@ -87,10 +87,10 @@ def _callevent(self, event, payload, abortable=True): # create reference player object for payload, if needed. if payload and ("playername" in payload) and ("player" not in payload): - for client in self.wrapper.servervitals.clients: + for client in self.wrapper.proxy.clients: if client.username == payload["playername"]: - if client.username not in self.wrapper.servervitals.players: - self.wrapper.servervitals.players[ + if client.username not in self.wrapper.players: + self.wrapper.players[ client.username] = Player(client.username, self.wrapper) payload["player"] = self.wrapper.api.minecraft.getPlayer( diff --git a/wrapper/core/mcserver.py b/wrapper/core/mcserver.py index 487426f8..41ae6049 100644 --- a/wrapper/core/mcserver.py +++ b/wrapper/core/mcserver.py @@ -41,11 +41,28 @@ # noinspection PyBroadException,PyUnusedLocal class MCServer(object): - def __init__(self, wrapper, servervitals): + def __init__(self, wrapper): self.log = wrapper.log self.config = wrapper.config - self.vitals = servervitals - + self.serverpath = self.config["General"]["server-directory"] + self.state = OFF + self.properties = {} + self.worldname = None + self.worldsize = 0 + # owner/op info + self.ownernames = {} + self.operator_list = [] + self.spammy_stuff = ["found nothing", "vehicle of", "Wrong location!", + "Tried to add entity", ] + # this is string name of the version, collected by console output + self.version = "" + self.version_compute = 0 + self.servericon = None + self.motd = None + self.timeofday = -1 + self.protocolVersion = -1 + self.server_port = "25564" + self.encoding = self.config["General"]["encoding"] self.stop_message = self.config["Misc"]["stop-message"] self.reboot_message = self.config["Misc"]["reboot-message"] @@ -64,7 +81,7 @@ def __init__(self, wrapper, servervitals): for part in commargs: if part[-4:] == ".jar": - self.args.append("%s/%s" % (self.vitals.serverpath, part)) + self.args.append("%s/%s" % (self.serverpath, part)) else: self.args.append(part) @@ -137,13 +154,13 @@ def init(self): capturethread.start() def __del__(self): - self.vitals.state = 0 + self.state = 0 def accepteula(self): - if os.path.isfile("%s/eula.txt" % self.vitals.serverpath): + if os.path.isfile("%s/eula.txt" % self.serverpath): self.log.debug("Checking EULA agreement...") - with open("%s/eula.txt" % self.vitals.serverpath) as f: + with open("%s/eula.txt" % self.serverpath) as f: eula = f.read() # if forced, should be at info level since acceptance @@ -152,7 +169,7 @@ def accepteula(self): self.log.warning( "EULA agreement was not accepted, accepting on" " your behalf...") - set_item("eula", "true", "eula.txt", self.vitals.serverpath) + set_item("eula", "true", "eula.txt", self.serverpath) self.log.debug("EULA agreement has been accepted.") return True @@ -181,7 +198,7 @@ def handle_server(self): command = self.args self.proc = subprocess.Popen( - command, cwd=self.vitals.serverpath, stdout=subprocess.PIPE, + command, cwd=self.serverpath, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) self.wrapper.players = {} @@ -230,7 +247,7 @@ def start(self): Start the Minecraft server """ self.server_autorestart = self.config["General"]["auto-restart"] - if self.vitals.state in (STARTED, STARTING): + if self.state in (STARTED, STARTING): self.log.warning("The server is already running!") return if not self.boot_server: @@ -247,21 +264,21 @@ def restart(self, reason=""): if reason == "": reason = self.restart_message - if self.vitals.state in (STOPPING, OFF): + if self.state in (STOPPING, OFF): self.start() return self.doserversaving() self.stop(reason) def kick_players(self, reasontext): - playerlist = copy.copy(self.vitals.players) + playerlist = copy.copy(self.wrapper.players) for player in playerlist: self.kick_player(player, reasontext) def kick_player(self, player, reasontext): if self.wrapper.proxymode: try: - playerclient = self.vitals.players[player].client + playerclient = self.wrapper.players[player].client playerclient.notify_disconnect(reasontext) except AttributeError: self.log.warning( @@ -302,10 +319,10 @@ def stop_server_command(self, reason="", restart_the_server=False): """ if reason == "": reason = self.stop_message - if self.vitals.state == OFF: + if self.state == OFF: self.log.warning("The server is not running... :?") return - if self.vitals.state == FROZEN: + if self.state == FROZEN: self.log.warning("The server is currently frozen.\n" "To stop it, you must /unfreeze it first") return @@ -317,7 +334,7 @@ def kill(self, reason="Killing Server"): """Forcefully kill the server. It will auto-restart if set in the configuration file. """ - if self.vitals.state in (STOPPING, OFF): + if self.state in (STOPPING, OFF): self.log.warning("The server is already dead, my friend...") return self.log.info("Killing Minecraft server with reason: %s", reason) @@ -334,7 +351,7 @@ def freeze(self, reason="Server is now frozen. You may disconnect."): specify None. This command currently only works for *NIX based systems. """ - if self.vitals.state != OFF: + if self.state != OFF: if os.name == "posix": self.log.info("Freezing server with reason: %s", reason) self.broadcast("&c%s" % reason) @@ -354,7 +371,7 @@ def unfreeze(self): to .freeze(reason) This command currently only works for *NIX based systems. """ - if self.vitals.state != OFF: + if self.state != OFF: if os.name == "posix": self.log.info("Unfreezing server (ignore any" " messages to type /start)...") @@ -375,7 +392,7 @@ def broadcast(self, message, who="@a"): string with formatting codes using the § as a prefix. """ if isinstance(message, dict): - if self.vitals.version_compute < 10700: + if self.version_compute < 10700: self.console("say %s %s" % (who, chattocolorcodes(message))) else: encoding = self.wrapper.encoding @@ -383,7 +400,7 @@ def broadcast(self, message, who="@a"): who, json.dumps(message, ensure_ascii=False))) else: temp = processcolorcodes(message) - if self.vitals.version_compute < 10700: + if self.version_compute < 10700: temp = processcolorcodes(message) self.console("say %s %s" % ( who, chattocolorcodes(temp))) @@ -394,17 +411,17 @@ def broadcast(self, message, who="@a"): def login(self, username, servereid, position, ipaddr): """Called when a player logs in.""" - if username not in self.vitals.players: - self.vitals.players[username] = Player(username, self.wrapper) + if username not in self.wrapper.players: + self.wrapper.players[username] = Player(username, self.wrapper) # store EID if proxy is not fully connected yet (or is not enabled). - self.vitals.players[username].playereid = servereid - self.vitals.players[username].loginposition = position - if self.vitals.players[username].ipaddress == "127.0.0.0": - self.vitals.players[username].ipaddress = ipaddr + self.wrapper.players[username].playereid = servereid + self.wrapper.players[username].loginposition = position + if self.wrapper.players[username].ipaddress == "127.0.0.0": + self.wrapper.players[username].ipaddress = ipaddr - if self.wrapper.proxy and self.vitals.players[username].client: - self.vitals.players[username].client.server_eid = servereid - self.vitals.players[username].client.position = position + if self.wrapper.proxy and self.wrapper.players[username].client: + self.wrapper.players[username].client.server_eid = servereid + self.wrapper.players[username].client.position = position # activate backup status self.wrapper.backups.idle = False @@ -441,8 +458,8 @@ def login(self, username, servereid, position, ipaddr): def logout(self, players_name): """Called when a player logs out.""" - if players_name in self.vitals.players: - player = self.vitals.players[players_name] + if players_name in self.wrapper.players: + player = self.wrapper.players[players_name] self.wrapper.events.callevent( "player.logout", {"player": player, "playername": players_name}, @@ -470,14 +487,14 @@ def logout(self, players_name): if player.client is None: player.abort = True - del self.vitals.players[players_name] + del self.wrapper.players[players_name] elif player.client.state != LOBBY and player.client.local: player.abort = True - del self.vitals.players[players_name] - - self.wrapper.proxy.removestaleclients() + del self.wrapper.players[players_name] + if self.wrapper.proxy: + self.wrapper.proxy.removestaleclients() - if len(self.vitals.players) == 0: + if len(self.wrapper.players) == 0: self.wrapper.backups.idle = True def getplayer(self, username): @@ -488,10 +505,12 @@ def getplayer(self, username): api.minecraft.getPlayer will deal in all players, including those in proxy and/or other hub servers. """ - if username in self.vitals.players: - player = self.vitals.players[username] + if username in self.wrapper.players: + player = self.wrapper.players[username] if player.client and player.client.state != LOBBY and player.client.local: # noqa return player + elif not self.wrapper.proxymode: + return player return False def reloadproperties(self): @@ -500,38 +519,30 @@ def reloadproperties(self): # straightforward and works in both PY2 and PY3 # Load server icon - if os.path.exists("%s/server-icon.png" % self.vitals.serverpath): - with open("%s/server-icon.png" % self.vitals.serverpath, "rb") as f: + if os.path.exists("%s/server-icon.png" % self.serverpath): + with open("%s/server-icon.png" % self.serverpath, "rb") as f: theicon = f.read() iconencoded = base64.standard_b64encode(theicon) - self.vitals.serverIcon = b"data:image/png;base64," + iconencoded + self.servericon = b"data:image/png;base64," + iconencoded - self.vitals.properties = config_to_dict_read( - "server.properties", self.vitals.serverpath) + self.properties = config_to_dict_read( + "server.properties", self.serverpath) - if self.vitals.properties == {}: + if self.properties == {}: self.log.warning("File 'server.properties' not found.") return False - if "level-name" in self.vitals.properties: - self.vitals.worldname = self.vitals.properties["level-name"] + if "level-name" in self.properties: + self.worldname = self.properties["level-name"] else: self.log.warning("No 'level-name=(worldname)' was" " found in the server.properties.") return False - self.vitals.motd = self.vitals.properties["motd"] - if "max-players" in self.vitals.properties: - self.vitals.maxPlayers = self.vitals.properties["max-players"] - else: - self.log.warning( - "No 'max-players=(count)' was found in the" - " server.properties. The default of '20' will be used.") - self.vitals.maxPlayers = 20 - self.vitals.onlineMode = self.vitals.properties["online-mode"] + self.motd = self.properties["motd"] def console(self, command): """Execute a console command on the server.""" - if self.vitals.state in (STARTING, STARTED, STOPPING) and self.proc: + if self.state in (STARTING, STARTED, STOPPING) and self.proc: self.proc.stdin.write("%s\n" % command) self.proc.stdin.flush() else: @@ -542,17 +553,17 @@ def changestate(self, state, reason=None): """Change the boot state indicator of the server, with a reason message. """ - self.vitals.state = state - if self.vitals.state == OFF: + self.state = state + if self.state == OFF: self.wrapper.events.callevent( "server.stopped", {"reason": reason}, abortable=False) - elif self.vitals.state == STARTING: + elif self.state == STARTING: self.wrapper.events.callevent( "server.starting", {"reason": reason}, abortable=False) - elif self.vitals.state == STARTED: + elif self.state == STARTED: self.wrapper.events.callevent( "server.started", {"reason": reason}, abortable=False) - elif self.vitals.state == STOPPING: + elif self.state == STOPPING: self.wrapper.events.callevent( "server.stopping", {"reason": reason}, abortable=False) self.wrapper.events.callevent( @@ -592,7 +603,7 @@ def server_reload(self): is in proxy mode, it will reconnect all clients to the serverconnection. """ - if self.vitals.state in (STOPPING, OFF): + if self.state in (STOPPING, OFF): self.log.warning( "The server is not already running... Just use '/start'.") return @@ -657,14 +668,14 @@ def read_ops_file(self, read_super_ops=True): """ ops = False # (4 = PROTOCOL_1_7 ) - 1.7.6 or greater use ops.json - if self.vitals.protocolVersion > 4: + if self.version_compute > 10700: ops = getjsonfile( - "ops", self.vitals.serverpath, encodedas=self.encoding + "ops", self.serverpath, encodedas=self.encoding ) if not ops: # try for an old "ops.txt" file instead. ops = [] - opstext = getfileaslines("ops.txt", self.vitals.serverpath) + opstext = getfileaslines("ops.txt", self.serverpath) if not opstext: return False for op in opstext: @@ -681,17 +692,17 @@ def read_ops_file(self, read_super_ops=True): # Grant "owner" an op level above 4. required for some wrapper commands if read_super_ops: for eachop in ops: - if eachop["name"] in self.vitals.ownernames: - eachop["level"] = self.vitals.ownernames[eachop["name"]] + if eachop["name"] in self.ownernames: + eachop["level"] = self.ownernames[eachop["name"]] return ops def refresh_ops(self, read_super_ops=True): - self.vitals.ownernames = config_to_dict_read("superops.txt", ".") - if self.vitals.ownernames == {}: + self.ownernames = config_to_dict_read("superops.txt", ".") + if self.ownernames == {}: sample = "=10\n=9" with open("superops.txt", "w") as f: f.write(sample) - self.vitals.operator_list = self.read_ops_file(read_super_ops) + self.operator_list = self.read_ops_file(read_super_ops) def getmemoryusage(self): """Returns allocated memory in bytes. This command @@ -767,21 +778,21 @@ def readconsole(self, buff): break line_words = buff.split(' ')[self.prepends_offset:] - self.vitals.version = getargs(line_words, 4) - semantics = self.vitals.version.split(".") + self.version = getargs(line_words, 4) + semantics = self.version.split(".") release = get_int(getargs(semantics, 0)) major = get_int(getargs(semantics, 1)) minor = get_int(getargs(semantics, 2)) - self.vitals.version_compute = minor + (major * 100) + (release * 10000) # noqa + self.version_compute = minor + (major * 100) + (release * 10000) # noqa - if len(self.vitals.version.split("w")) > 1: + if len(self.version.split("w")) > 1: # It is a snap shot - self.vitals.version_compute = 10800 + self.version_compute = 10800 # 1.7.6 (protocol 5) is the cutoff where ops.txt became ops.json - if self.vitals.version_compute > 10705 and self.vitals.protocolVersion < 0: # noqa - self.vitals.protocolVersion = 5 + if self.version_compute > 10705 and self.protocolVersion < 0: # noqa + self.protocolVersion = 5 self.wrapper.api.registerPermission("mc1.7.6", value=True) - if self.vitals.version_compute < 10702 and self.wrapper.proxymode: + if self.version_compute < 10702 and self.wrapper.proxymode: self.log.warning("\nProxy mode cannot run because the " "server is a pre-Netty version:\n\n" "http://wiki.vg/Protocol_version_numbers" @@ -818,33 +829,36 @@ def readconsole(self, buff): prefix = " ".join(buff.split(' ')[:self.prepends_offset]) if not self.wrapper.wrapper_onlinemode: + try: + pport = "either port %s or " % self.wrapper.proxy.proxy_port + except AttributeError: + pport = "" message = ( "%s Since you are running Wrapper in OFFLINE mode, THIS " "COULD BE SERIOUS!\n%s Wrapper is not handling any" " authentication.\n%s This is only ok if this wrapper " - "is not accessible from either port %s or port %s" + "is not accessible from %sport %s" " (I.e., this wrapper is a multiworld for a hub server, or" " you are doing your own authorization via a plugin)." % ( - prefix, prefix, prefix, - self.vitals.server_port, self.wrapper.proxy.proxy_port)) + prefix, prefix, prefix, pport, self.server_port)) else: message = ( "%s Since you are running Wrapper in proxy mode, this" " should be ok because Wrapper is handling the" " authentication, PROVIDED no one can access port" " %s from outside your network." % ( - prefix, self.vitals.server_port)) + prefix, self.server_port)) if self.wrapper.proxymode: buff = message # read port of server and display proxy port, if applicable if "Starting Minecraft server on" in buff: - self.vitals.server_port = get_int(buff.split(':')[-1:][0]) + self.server_port = get_int(buff.split(':')[-1:][0]) # check for server console spam before printing to wrapper console server_spaming = False - for things in self.vitals.spammy_stuff: + for things in self.spammy_stuff: if things in buff: server_spaming = True @@ -868,8 +882,8 @@ def readconsole(self, buff): # Getting world name elif "Preparing level" in buff: - self.vitals.worldname = getargs(line_words, 2).replace('"', "") - self.world = World(self.vitals.worldname, self) + self.worldname = getargs(line_words, 2).replace('"', "") + self.world = World(self.worldname, self) # Player Message elif first_word[0] == "<": @@ -988,13 +1002,16 @@ def readconsole(self, buff): elif second_word == "Teleported" and getargs(line_words, 3) == "to": playername = getargs(line_words, 2) # [SurestTexas00: Teleported SapperLeader to 48.49417131908783, 77.67081086259394, -279.88880690937475] # noqa - if playername in self.wrapper.servervitals.players: + if playername in self.wrapper.players: playerobj = self.getplayer(playername) - playerobj._position = [ - get_int(float(getargs(line_words, 4).split(",")[0])), - get_int(float(getargs(line_words, 5).split(",")[0])), - get_int(float(getargs(line_words, 6).split("]")[0])), 0, 0 - ] + try: + playerobj._position = [ + get_int(float(getargs(line_words, 4).split(",")[0])), + get_int(float(getargs(line_words, 5).split(",")[0])), + get_int(float(getargs(line_words, 6).split("]")[0])), 0, 0 + ] + except ValueError: + pass self.wrapper.events.callevent( "player.teleport", {"player": playerobj}, abortable=False) @@ -1018,13 +1035,16 @@ def readconsole(self, buff): elif first_word == "Teleported" and getargs(line_words, 2) == "to": playername = second_word # Teleported SurestTexas00 to 48.49417131908783, 77.67081086259394, -279.88880690937475 # noqa - if playername in self.wrapper.servervitals.players: + if playername in self.wrapper.players: playerobj = self.getplayer(playername) - playerobj._position = [ - get_int(float(getargs(line_words, 3).split(",")[0])), - get_int(float(getargs(line_words, 4).split(",")[0])), - get_int(float(getargs(line_words, 5))), 0, 0 - ] + try: + playerobj._position = [ + get_int(float(getargs(line_words, 3).split(",")[0])), + get_int(float(getargs(line_words, 4).split(",")[0])), + get_int(float(getargs(line_words, 5))), 0, 0 + ] + except ValueError: + pass self.wrapper.events.callevent( "player.teleport", {"player": playerobj}, abortable=False) @@ -1052,7 +1072,7 @@ def reboot_timer(self): while not self.wrapper.haltsig.halt: time.sleep(1) timer = rb_mins - rb_mins_warn - while self.vitals.state in (STARTED, STARTING): + while self.state in (STARTED, STARTING): timer -= 1 time.sleep(60) if timer > 0: @@ -1094,17 +1114,17 @@ def reboot_timer(self): def eachsecond_web(self): if time.time() - self.lastsizepoll > 120: - if self.vitals.worldname is None: + if self.worldname is None: return True self.lastsizepoll = time.time() size = 0 # os.scandir not in standard library on early py2.7.x systems for i in os.walk( - "%s/%s" % (self.vitals.serverpath, self.vitals.worldname) + "%s/%s" % (self.serverpath, self.worldname) ): for f in os.listdir(i[0]): size += os.path.getsize(os.path.join(i[0], f)) - self.vitals.worldsize = size + self.worldsize = size def _console_event(self, payload): """This function is used in conjunction with event handlers to diff --git a/wrapper/core/servervitals.py b/wrapper/core/servervitals.py deleted file mode 100644 index e8108536..00000000 --- a/wrapper/core/servervitals.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (C) 2016, 2017 - BenBaptist and Wrapper.py developer(s). -# https://github.com/benbaptist/minecraft-wrapper -# This program is distributed under the terms of the GNU -# General Public License, version 3 or later. - - -class ServerVitals(object): - """ Centralized location for server information. This class also - permits sharing of server information between the caller (such as - a Wrapper instance) and proxy.""" - def __init__(self, playerobjects): - - # operational info - self.serverpath = "" - self.state = 0 - self.server_port = "25564" - self.command_prefix = "/" - - # Shared data structures and run-time - self.players = playerobjects - - # TODO - I don't think this is used or needed (same name as proxy.entity_control!) - self.entity_control = None - # -1 until a player logs on and server sends a time update - self.timeofday = -1 - self.spammy_stuff = ["found nothing", "vehicle of", "Wrong location!", - "Tried to add entity", ] - - # PROPOSE - self.clients = [] - - # owner/op info - self.ownernames = {} - self.operator_list = [] - - # server properties and folder infos - self.properties = {} - self.worldname = None - self.worldsize = 0 - self.maxplayers = 20 - self.motd = None - self.serverIcon = None - - # # Version information - # -1 until proxy mode checks the server's MOTD on boot - self.protocolVersion = -1 - # this is string name of the version, collected by console output - self.version = "" - # a comparable number = x0y0z, where x, y, z = release, - # major, minor, of version. - self.version_compute = 0 diff --git a/wrapper/core/wrapper.py b/wrapper/core/wrapper.py index 829f61a0..2737bac8 100644 --- a/wrapper/core/wrapper.py +++ b/wrapper/core/wrapper.py @@ -17,7 +17,11 @@ import logging # import smtplib import sys # used to pass sys.argv to server -# from pprint import pprint + +try: + from concurrent.futures import ProcessPoolExecutor +except ImportError: + ProcessPoolExecutor = False # non standard library imports try: @@ -51,7 +55,7 @@ from core.irc import IRC from core.scripts import Scripts import core.buildinfo as buildinfo -from proxy.utils.mcuuid import UUIDS +from proxy.utils.mcuuid import UUIDS, MCUUID from core.config import Config from core.backups import Backups from core.consoleuser import ConsolePlayer @@ -60,8 +64,7 @@ from utils.crypt import Crypt, phrase_to_url_safebytes, gensalt # optional API type stuff -from core.servervitals import ServerVitals -from proxy.base import Proxy, ProxyConfig, HaltSig +from proxy.base import Proxy from api.base import API import management.web as manageweb @@ -188,9 +191,8 @@ def __init__(self, secret_passphrase): # the value of self.haltsig would not necessarily change the value of # the passed parameter (unless it was specifically referenced back as # `wrapper.haltsig`). Since the halt signal needs to be passed, possibly - # several layers deep, and into modules that it may be desireable to - # not have direct access to wrapper, using a HaltSig object is - # required to ensure we are actually sharing the same value between + # several layers deep, and into other modules , using a HaltSig object + # is required to ensure we are actually sharing the same value between # different objects. self.haltsig = HaltSig() @@ -207,6 +209,7 @@ def __init__(self, secret_passphrase): # core functions and datasets self.perms = Permissions(self) self.uuids = UUIDS(self.log, self.usercache) + self.mcuuid = MCUUID self.plugins = Plugins(self) self.commands = Commands(self) self.events = Events(self) @@ -237,24 +240,6 @@ def __init__(self, secret_passphrase): " console functioning. Press to acknowledge...") sys.stdin.readline() - # create server/proxy vitals and config objects - self.servervitals = ServerVitals(self.players) - - # LETS TAKE A SECOND TO DISCUSS PLAYER OBJECTS: - # The ServerVitals class gets passed the player object list now, but - # player objects are now housed in wrapper. This is how we are - # passing information between proxy and wrapper. - - self.servervitals.serverpath = self.config[ - "General"]["server-directory"] - self.servervitals.state = OFF - self.servervitals.command_prefix = self.config[ - "Proxy"]["command-prefix"] - - self.proxyconfig = ProxyConfig() - self.proxyconfig.proxy = self.config["Proxy"] - self.proxyconfig.entity = self.config["Entities"] - def __del__(self): """prevent error message on very first wrapper starts when wrapper exits after creating new wrapper.properties file. @@ -281,7 +266,7 @@ def start(self): self._registerwrappershelp() # The MCServerclass is a console wherein the server is started - self.javaserver = MCServer(self, self.servervitals) + self.javaserver = MCServer(self) self.javaserver.init() # load plugins @@ -403,7 +388,7 @@ def sigtstp(*args): self._halt() def _halt(self): - if self.servervitals.state in (1, 2): + if self.javaserver.state in (1, 2): self.javaserver.stop(self.halt_message, restart_the_server=False) self.haltsig.halt = True @@ -812,7 +797,7 @@ def isonlinemode(self): return self.config["Proxy"]["online-mode"] if self.javaserver: try: - return self.servervitals.properties["online-mode"] + return self.javaserver.properties["online-mode"] except KeyError: pass return False @@ -844,9 +829,7 @@ def _start_emailer(self): def _startproxy(self): try: - self.proxy = Proxy(self.haltsig, self.proxyconfig, self.servervitals, - self.log, self.wrapper_usercache, self.events, - self.encoding) + self.proxy = Proxy(self) except ImportError: self.log.error("Proxy mode not started because of missing " "dependencies for encryption!") @@ -855,7 +838,7 @@ def _startproxy(self): # wait for server to start timer = 0 - while self.servervitals.state < STARTED: + while self.javaserver.state < STARTED: timer += 1 time.sleep(.1) if timer > 1200: @@ -866,15 +849,19 @@ def _startproxy(self): self.disable_proxymode() return - if self.proxy.proxy_port == self.servervitals.server_port: + if self.proxy.proxy_port == self.javaserver.server_port: self.log.warning("Proxy mode cannot start because the wrapper" " port is identical to the server port.") self.disable_proxymode() return - proxythread = threading.Thread(target=self.proxy.host, args=()) - proxythread.daemon = True - proxythread.start() + if not ProcessPoolExecutor: + proxythread = threading.Thread(target=self.proxy.host, args=()) + proxythread.daemon = True + proxythread.start() + else: + with ProcessPoolExecutor(max_workers=2) as executor: + executor.submit(self.proxy.host()) def disable_proxymode(self): self.proxymode = False @@ -1340,3 +1327,16 @@ def _show_help_bans(self, player): " displays on single page!)", separator="[players|ips] [searchtext] ", pad=12, usereadline=self.use_readline, player=player) + + +class HaltSig(object): + """ + HaltSig is simply a sort of dummy class created for the + proxy. proxy expects this object with a self.halt property + that tells proxy to shutdown. The caller maintains control + of the Haltsig object and uses it to signal the proxy to + shut down. The caller will import this class, instantiate + it, and then pass the object to proxy as the argument for + termsignal.""" + def __init__(self): + self.halt = False diff --git a/wrapper/management/web.py b/wrapper/management/web.py index 4d4e4983..afdc390d 100644 --- a/wrapper/management/web.py +++ b/wrapper/management/web.py @@ -696,8 +696,8 @@ def run_action(self, request): last_refresh = time.time() players = [] refresh_time = time.time() - for i in self.wrapper.servervitals.players: - player = self.wrapper.servervitals.players[i] + for i in self.wrapper.players: + player = self.wrapper.players[i] players.append({ "name": i, "loggedIn": player.loggedIn, @@ -743,17 +743,17 @@ def run_action(self, request): mem_use = self.wrapper.memory_usage() wrapper_peak_mem = mem_use["peak"] * 1000 wrapper_rss_mem = mem_use["rss"] * 1000 - stats = {"playerCount": (len(self.wrapper.servervitals.players), - self.wrapper.servervitals.maxplayers), + stats = {"playerCount": (len(self.wrapper.players), + self.wrapper.proxy.maxplayers), "players": players, "plugins": plugins, - "server_state": self.wrapper.servervitals.state, + "server_state": self.wrapper.javaserver.state, "wrapper_build": self.wrapper.getbuildstring(), "console": console_scrollback, "chat": chat_scrollback, - "level_name": self.wrapper.servervitals.worldname, - "server_version": self.wrapper.servervitals.version, - "motd": self.wrapper.servervitals.motd, + "level_name": self.wrapper.javaserver.worldname, + "server_version": self.wrapper.javaserver.version, + "motd": self.wrapper.javaserver.motd, "last_refresh": refresh_time, "disk_avail": self.web.getdisk_usage(), "server_name": self.config["Web"]["server-name"], @@ -761,7 +761,7 @@ def run_action(self, request): "wrapper_memory_rss": wrapper_rss_mem, "wrapper_memory_peak": wrapper_peak_mem, "server_memory_graph": memory_graph, - "world_size": self.wrapper.servervitals.worldsize + "world_size": self.wrapper.javaserver.worldsize } return stats diff --git a/wrapper/proxy/base.py b/wrapper/proxy/base.py index 82762651..d4a59d68 100644 --- a/wrapper/proxy/base.py +++ b/wrapper/proxy/base.py @@ -20,11 +20,9 @@ from utils.py23 import py_str from proxy.utils.constants import * -from proxy.utils import mcuuid from proxy.entity.entitycontrol import EntityControl # encryption requires 'cryptography' package. - try: import proxy.utils.encryption as encryption except ImportError: @@ -50,101 +48,20 @@ Client = False Packet = False -""" The whole point of what follows was originally intended to -support making proxy an independent thing that does not need -a wrapper or server instance to function. Not sure it was a -great idea, but... We shall see.""" - -class NullEventHandler(object): - def __init__(self): - pass +class Proxy(object): + def __init__(self, wrapper): + self.wrapper = wrapper + self.javaserver = self.wrapper.javaserver + self.encoding = self.wrapper.encoding + self.config = self.wrapper.config["Proxy"] + self.ent_config = self.wrapper.config["Entities"] + self.log = self.wrapper.log - def callevent(self, event, payload): - """ - An event handler must have this method that expects - two positional arguments: - :event: The string name of the event. - :payload: A dictionary of items describing the event (varies - with event. - """ - pass - - -class HaltSig(object): - """ - HaltSig is simply a sort of dummy class created for the - proxy. proxy expects this object with a self.halt property - that tells proxy to shutdown. The caller maintains control - of the Haltsig object and uses it to signal the proxy to - shut down. The caller will import this class, instantiate - it, and then pass the object to proxy as the argument for - termsignal.""" - def __init__(self): - self.halt = False - - -class ProxyConfig(object): - def __init__(self): - self.proxy = { - "auto-name-changes": True, - "hidden-ops": [], - "max-players": 1024, - "online-mode": True, - "proxy-bind": "0.0.0.0", - "proxy-enabled": True, - "proxy-port": 25570, - "silent-ipban": True, - } - self.entity = { - "enable-entity-controls": False, - "entity-update-frequency": 4, - "thin-chicken": 30, - "thin-cow": 40, - "thin-sheep": 40, - "thin-zombie_pigman": 200, - "thinning-activation-threshhold": 100, - "thinning-frequency": 30 - } - - -""" -class ServerVitals(object): - def __init__(self, playerobjects): - self.serverpath = "" - self.state = 0 - self.server_port = "25564" - self.command_prefix = "/" - self.players = playerobjects - self.entity_control = None - self.timeofday = -1 - self.spammy_stuff = ["found nothing", "vehicle of", "Wrong location!", - "Tried to add entity", ] self.clients = [] - self.ownernames = {} - self.operator_list = [] - self.properties = {} - self.worldname = None - self.worldsize = 0 self.maxplayers = 20 - self.motd = None - self.serverIcon = None - self.protocolVersion = -1 - self.version = "" - self.version_compute = 0 -""" - - -class Proxy(object): - def __init__(self, termsignal, config, servervitals, loginstance, - usercache_object, eventhandler, encoding="utf-8"): + self.command_prefix = self.config["command-prefix"] - self.encoding = encoding - self.srv_data = servervitals - self.config = config.proxy - self.ent_config = config.entity - - self.log = loginstance # encryption = False if proxy.utils.encryption does not import if not encryption and self.config["proxy-enabled"]: self.log.error(importerror) @@ -157,22 +74,19 @@ def __init__(self, termsignal, config, servervitals, loginstance, "esources for possible solutions") raise ImportError() - self.usercache = usercache_object.Data - self.usercache_obj = usercache_object - self.eventhandler = eventhandler - self.uuids = mcuuid.UUIDS(self.log, self.usercache) + self.usercache_obj = self.wrapper.wrapper_usercache + self.usercache = self.usercache_obj.Data + self.eventhandler = self.wrapper.events + self.uuids = self.wrapper.uuids - # termsignal is an object with a `halt` property set to True/False - # it represents the calling program's run status - self.caller = termsignal - # Proxy's run status (set True to shutdown/ end `host()` while loop + # Proxy's run status (set True to shutdown/ end `host()` while loop) self.abort = False # self assignments (gets specific values) self.proxy_bind = self.config["proxy-bind"] self.proxy_port = int(self.config["proxy-port"]) self.silent_ip_banning = self.config["silent-ipban"] - self.srv_data.maxPlayers = self.config["max-players"] + self.maxlayers = self.config["max-players"] self.proxy_worlds = self.config["worlds"] self.usehub = self.config["built-in-hub"] self.onlinemode = self.config["online-mode"] @@ -212,7 +126,7 @@ def host(self): server is fully up and running.""" # loops while server is not started (STARTED = 2) - while not self.srv_data.state == 2: + while not self.javaserver.state == 2: time.sleep(1) # get the protocol version from the server @@ -223,8 +137,8 @@ def host(self): "check server/wrapper configs? (%s)", e) args = [-1, "none", False] - self.srv_data.protocolVersion = args[0] - self.srv_data.version = args[1] + self.javaserver.protocolVersion = args[0] + self.javaserver.version = args[1] self.forge = args[2] if self.forge: self.mod_info["modinfo"] = args[3] @@ -247,7 +161,7 @@ def host(self): self.entity_control = EntityControl(self) # accept clients and start their threads - while not (self.abort or self.caller.halt): + while not (self.abort or self.wrapper.haltsig.halt): try: sock, addr = self.proxy_socket.accept() except Exception as e: @@ -259,6 +173,7 @@ def host(self): if self.silent_ip_banning and banned_ip: # 0: done receiving, 1: done sending, 2: both sock.shutdown(2) + sock.close() self.log.info("Someone tried to connect from a banned ip:" " %s (connection refused)", addr) continue @@ -272,11 +187,11 @@ def host(self): def removestaleclients(self): """removes aborted client and player objects""" - for i, client in enumerate(self.srv_data.clients): - if self.srv_data.clients[i].abort: - if self.srv_data.clients[i].username in self.srv_data.players: - del self.srv_data.players[self.srv_data.clients[i].username] - self.srv_data.clients.pop(i) + for i, client in enumerate(self.clients): + if self.clients[i].abort: + if self.clients[i].username in self.wrapper.players: + del self.wrapper.players[self.clients[i].username] + self.clients.pop(i) def pollserver(self, host="localhost", port=None): """ @@ -289,7 +204,7 @@ def pollserver(self, host="localhost", port=None): modinfo (if Forge) ] """ if port is None: - port = self.srv_data.server_port + port = self.javaserver.server_port server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -303,7 +218,7 @@ def pollserver(self, host="localhost", port=None): # Disconnect packet.sendpkt(0x00, [NULL, ], ["", ]) packet.flush() - self.srv_data.protocolVersion = -1 + self.javaserver.protocolVersion = -1 container = [] while True: pkid, packet_tuple = packet.grabpacket() @@ -352,7 +267,9 @@ def use_newname(self, oldname, newname, realuuid, client): old_local_uuid = self.uuids.getuuidfromname(oldname) new_local_uuid = self.uuids.getuuidfromname(newname) cwd = "%s/%s" % ( - self.srv_data.serverpath, self.srv_data.worldname) + self.javaserver.serverpath, + self.javaserver.worldname + ) self.uuids.convert_files(old_local_uuid, new_local_uuid, cwd) self.usercache[realuuid]["localname"] = newname client.info["username"] = newname @@ -366,7 +283,7 @@ def getclientbyofflineserveruuid(self, uuid): :return: the matching client """ attempts = ["Search: %s" % str(uuid)] - for client in self.srv_data.clients: + for client in self.clients: attempts.append("try: client-%s uuid-%s serveruuid-%s name-%s" % (client, client.wrapper_uuid.string, client.local_uuid.string, client.username)) @@ -375,7 +292,7 @@ def getclientbyofflineserveruuid(self, uuid): return client self.log.debug("getclientbyofflineserveruuid failed: \n %s", attempts) - self.log.debug("POSSIBLE CLIENTS: \n %s", self.srv_data.clients) + self.log.debug("POSSIBLE CLIENTS: \n %s", self.clients) return False # no client def banplayer(self, playername, reason="Banned by an operator", @@ -398,7 +315,9 @@ def getuuidbanreason(self, uuid): :param uuid: uuid of player as string :return: string representing ban reason """ - banlist = getjsonfile("banned-players", self.srv_data.serverpath) + banlist = getjsonfile( + "banned-players", self.javaserver.serverpath + ) if banlist: banrecord = find_in_json(banlist, "uuid", uuid) return "%s by %s" % (banrecord["reason"], banrecord["source"]) @@ -418,7 +337,9 @@ def banuuid(self, uuid, reason="The Ban Hammer has spoken!", This probably only works on 1.7.10 servers or later """ - banlist = getjsonfile("banned-players", self.srv_data.serverpath) + banlist = getjsonfile( + "banned-players", self.javaserver.serverpath + ) if banlist is not False: # file and directory exist. if banlist is None: # file was empty or not valid banlist = dict() # ensure valid dict before operating on it @@ -442,7 +363,7 @@ def banuuid(self, uuid, reason="The Ban Hammer has spoken!", "reason": reason}) if putjsonfile(banlist, "banned-players", - self.srv_data.serverpath): + self.javaserver.serverpath): # this actually is not needed. Commands now handle the kick. console_command = "kick %s %s" % (name, reason) self.run_command(console_command) @@ -467,7 +388,9 @@ def banuuidraw(self, uuid, username, reason="The Ban Hammer has spoken!", This probably only works on 1.7.10 servers or later """ - banlist = getjsonfile("banned-players", self.srv_data.serverpath) + banlist = getjsonfile( + "banned-players", self.javaserver.serverpath + ) if banlist is not False: # file and directory exist. if banlist is None: # file was empty or not valid banlist = dict() # ensure valid dict before operating on it @@ -490,7 +413,7 @@ def banuuidraw(self, uuid, username, reason="The Ban Hammer has spoken!", "reason": reason}) if putjsonfile(banlist, "banned-players", - self.srv_data.serverpath): + self.javaserver.serverpath): self.log.info("kicking %s... %s", username, reason) console_command = "kick %s Banned: %s" % (username, reason) @@ -517,7 +440,7 @@ def banip(self, ipaddress, reason="The Ban Hammer has spoken!", """ if not isipv4address(ipaddress): return "Invalid IPV4 address: %s" % ipaddress - banlist = getjsonfile("banned-ips", self.srv_data.serverpath) + banlist = getjsonfile("banned-ips", self.javaserver.serverpath) if banlist is not False: # file and directory exist. if banlist is None: # file was empty or not valid banlist = dict() # ensure valid dict before operating on it @@ -537,9 +460,10 @@ def banip(self, ipaddress, reason="The Ban Hammer has spoken!", "source": source, "expires": expiration, "reason": reason}) - if putjsonfile(banlist, "banned-ips", self.srv_data.serverpath): + if putjsonfile(banlist, "banned-ips", + self.javaserver.serverpath): banned = "" - for client in self.srv_data.clients: + for client in self.clients: if client.ip == str(ipaddress): console_command = "kick %s Your IP is Banned!" % client.username # noqa @@ -555,7 +479,7 @@ def banip(self, ipaddress, reason="The Ban Hammer has spoken!", def pardonip(self, ipaddress): if not isipv4address(ipaddress): return "Invalid IPV4 address: %s" % ipaddress - banlist = getjsonfile("banned-ips", self.srv_data.serverpath) + banlist = getjsonfile("banned-ips", self.javaserver.serverpath) if banlist is not False: # file and directory exist. if banlist is None: # file was empty or not valid return "No IP bans have ever been recorded." @@ -564,7 +488,8 @@ def pardonip(self, ipaddress): for x in banlist: if x == banrecord: banlist.remove(x) - if putjsonfile(banlist, "banned-ips", self.srv_data.serverpath): + if putjsonfile(banlist, "banned-ips", + self.javaserver.serverpath): return "pardoned %s" % ipaddress return "Could not write banlist to disk" else: @@ -574,7 +499,9 @@ def pardonip(self, ipaddress): return "Banlist not found on disk" # error text def pardonuuid(self, uuid): - banlist = getjsonfile("banned-players", self.srv_data.serverpath) + banlist = getjsonfile( + "banned-players", self.javaserver.serverpath + ) if banlist is not False: # file and directory exist. if banlist is None: # file was empty or not valid return "No bans have ever been recorded..?" @@ -585,7 +512,7 @@ def pardonuuid(self, uuid): banlist.remove(x) if putjsonfile(banlist, "banned-players", - self.srv_data.serverpath): + self.javaserver.serverpath): name = self.uuids.getusernamebyuuid(str(uuid)) return "pardoned %s" % name return "Could not write banlist to disk" @@ -595,7 +522,9 @@ def pardonuuid(self, uuid): return "Banlist not found on disk" # error text def pardonname(self, username): - banlist = getjsonfile("banned-players", self.srv_data.serverpath) + banlist = getjsonfile( + "banned-players", self.javaserver.serverpath + ) if banlist is not False: # file and directory exist. if banlist is None: # file was empty or not valid return "No bans have ever been recorded..?" @@ -606,7 +535,7 @@ def pardonname(self, username): banlist.remove(x) if putjsonfile(banlist, "banned-players", - self.srv_data.serverpath): + self.javaserver.serverpath): return "pardoned %s" % username return "Could not write banlist to disk" else: @@ -615,7 +544,9 @@ def pardonname(self, username): return "Banlist not found on disk" # error text def isuuidbanned(self, uuid): # Check if the UUID of the user is banned - banlist = getjsonfile("banned-players", self.srv_data.serverpath) + banlist = getjsonfile( + "banned-players", self.javaserver.serverpath + ) if banlist: # make sure banlist exists banrecord = find_in_json(banlist, "uuid", str(uuid)) if banrecord: @@ -635,7 +566,7 @@ def isuuidbanned(self, uuid): # Check if the UUID of the user is banned return False # banlist empty or record not found def isipbanned(self, ipaddress): # Check if the IP address is banned - banlist = getjsonfile("banned-ips", self.srv_data.serverpath) + banlist = getjsonfile("banned-ips", self.javaserver.serverpath) if banlist: # make sure banlist exists for record in banlist: _ip = record["ip"] diff --git a/wrapper/proxy/client/clientconnection.py b/wrapper/proxy/client/clientconnection.py index 9da4ce33..d046348c 100644 --- a/wrapper/proxy/client/clientconnection.py +++ b/wrapper/proxy/client/clientconnection.py @@ -27,7 +27,6 @@ from proxy.packets import mcpackets_cb from proxy.utils.constants import * -from proxy.utils.mcuuid import MCUUID from api.helpers import processcolorcodes, getjsonfile, putjsonfile @@ -44,8 +43,7 @@ class Client(object): client (self.packet.sendpkt()) Client receives the parent proxy as it's argument. - No longer receives the proxy's wrapper instance! All - data is passed via srv_data from proxy's srv_data. + """ def __init__(self, proxy, clientsock, client_addr, banned=False): @@ -53,9 +51,10 @@ def __init__(self, proxy, clientsock, client_addr, banned=False): self.client_socket = clientsock self.client_address = client_addr self.proxy = proxy + self.wrapper = self.proxy.wrapper + self.javaserver = self.wrapper.javaserver self.public_key = self.proxy.public_key self.private_key = self.proxy.private_key - self.srv_data = self.proxy.srv_data self.log = self.proxy.log self.ipbanned = banned @@ -76,9 +75,9 @@ def __init__(self, proxy, clientsock, client_addr, banned=False): self.MOTD = {} # client will reset this later, if need be.. - self.clientversion = self.srv_data.protocolVersion + self.clientversion = self.javaserver.protocolVersion # default server port (to this wrapper's server) - self.serverport = self.srv_data.server_port + self.serverport = self.javaserver.server_port # packet stuff self.pktSB = mcpackets_sb.Packets(self.clientversion) @@ -221,7 +220,7 @@ def notify_disconnect(self, message): ) time.sleep(.4) self.disc_request = False - self.change_servers("127.0.0.1", self.serverport) + self.change_servers("localhost", self.serverport) else: self.disc_request = True @@ -268,15 +267,11 @@ def handle(self): self._close_server_instance("Client Handle Ended") try: self.client_socket.shutdown(2) - except AttributeError: - self.log.debug( - "(%s, %s) handle aborted and self.client_socket has no" - " shutdown attribute", self.username, self.ip - ) - except socket_error: + self.client_socket.close() + except (AttributeError, socket_error): self.log.debug( - "(%s, %s) handle aborted and self.client_socket experienced a " - "socket error while doing 'shutdown'.", self.username, self.ip + "(%s, %s) handle ending and client_socket does not " + "exist any longer", self.username, self.ip ) def _flush_loop(self): @@ -306,7 +301,7 @@ def _flush_loop(self): if self.username != "PING REQUEST": self.log.debug("%s clientconnection _flush_loop thread ended", self.username) - self.proxy.removestaleclients() # from proxy.srv_data.clients + self.proxy.removestaleclients() # from proxy.clients def _parse(self, pkid): """ @@ -398,6 +393,7 @@ def _parse_handshaking_legacy(self): ) self.packet.send_raw(0xff+0x00+0x00+0x00) self.client_socket.shutdown(2) + self.client_socket.close() self.abort = True def _parse_handshaking(self): @@ -443,20 +439,20 @@ def _parse_handshaking(self): # and cannot be sure of the info itself if not self.onlinemode: # Spigot and Wrapper share this in common: - self.mojanguuid = MCUUID(splitaddress[2]) + self.mojanguuid = self.proxy.wrapper.mcuuid(splitaddress[2]) self.info["realuuid"] = self.mojanguuid.string self.ip = splitaddress[1] if len(splitaddress) > 3 and splitaddress[3] == "WPY": self.info["client-is-wrapper"] = True - if self.srv_data.protocolVersion == -1: + if self.javaserver.protocolVersion == -1: # ... returns -1 to signal no server self.disconnect( "The server is not started (protocol not established)." ) return False - if not self.srv_data.state == 2: + if not self.javaserver.state == 2: self.disconnect( "Server has not finished booting. Please try" " connecting again in a few seconds" @@ -467,7 +463,7 @@ def _parse_handshaking(self): self.disconnect("You're running an unsupported snapshot" " (protocol: %s)!" % self.clientversion) return False - if self.srv_data.protocolVersion != self.clientversion: + if self.javaserver.protocolVersion != self.clientversion: self.disconnect("You're not running the same Minecraft" " version as the server!") return False @@ -499,16 +495,16 @@ def _parse_status_request(self): back to HANDSHAKE mode. """ sample = [] - for player in self.srv_data.players: - playerobj = self.srv_data.players[player] + for player in self.proxy.wrapper.players: + playerobj = self.proxy.wrapper.players[player] if playerobj.username not in self.hidden_ops: sample.append({"name": playerobj.username, "id": str(playerobj.mojangUuid)}) if len(sample) > 5: break - reported_version = self.srv_data.protocolVersion - reported_name = self.srv_data.version - motdtext = self.srv_data.motd + reported_version = self.javaserver.protocolVersion + reported_name = self.javaserver.version + motdtext = self.javaserver.motd if self.clientversion >= PROTOCOL_1_8START: motdtext = processcolorcodes(motdtext.replace( "\\", "")) @@ -516,7 +512,7 @@ def _parse_status_request(self): "description": motdtext, "players": { "max": int(self.proxy.config["max-players"]), - "online": len(self.srv_data.players), + "online": len(self.proxy.wrapper.players), "sample": sample }, "version": { @@ -526,8 +522,8 @@ def _parse_status_request(self): } # add Favicon, if it exists - if self.srv_data.serverIcon: - self.MOTD["favicon"] = self.srv_data.serverIcon + if self.javaserver.servericon: + self.MOTD["favicon"] = self.javaserver.servericon # add Forge information, if applicable. if self.proxy.forge: @@ -625,7 +621,7 @@ def _parse_login_encr_response(self): """ # read response Tokens - "shared_secret|verify_token" - if self.srv_data.protocolVersion < 6: + if self.javaserver.protocolVersion < 6: data = self.packet.readpkt([BYTEARRAY_SHORT, BYTEARRAY_SHORT]) else: data = self.packet.readpkt([BYTEARRAY, BYTEARRAY]) @@ -684,7 +680,7 @@ def _logon_client_into_proxy(self): return # add client (or disconnect if full - if len(self.proxy.srv_data.clients) < self.proxy.config["max-players"]: + if len(self.proxy.clients) < self.proxy.config["max-players"]: self._add_client() else: uuids = getjsonfile( @@ -744,7 +740,7 @@ def _logon_client_into_proxy(self): self.state = HANDSHAKE self.disconnect("Login denied by a Plugin.") - del self.srv_data.players[self.username] + del self.proxy.wrapper.players[self.username] return self.permit_disconnect_from_server = True @@ -757,8 +753,8 @@ def _logon_client_into_proxy(self): # set compression # compression was at the bottom of _login_authenticate_client... if self.clientversion >= PROTOCOL_1_8START: - if "network-compression-threshold" in self.proxy.srv_data.properties: # noqa - comp = self.proxy.srv_data.properties[ + if "network-compression-threshold" in self.javaserver.properties: # noqa + comp = self.javaserver.properties[ "network-compression-threshold"] self.packet.sendpkt( self.pktCB.LOGIN_SET_COMPRESSION[PKT], [VARINT], [comp]) @@ -873,7 +869,7 @@ def _close_server_instance(self, term_message): if self.server_connection: self.server_connection.close_server(term_message) - def change_servers(self, ip="127.0.0.1", port=25600): + def change_servers(self, ip="localhost", port=25600): """ Leaves the current proxy server connection and attempts a new server connection. If it fails, it attempts to re-connect @@ -1248,7 +1244,7 @@ def _login_authenticate_client(self, server_id): # } requestdata = r.json() playerid = requestdata["id"] - self.wrapper_uuid = MCUUID(playerid) + self.wrapper_uuid = self.proxy.wrapper.mcuuid(playerid) if requestdata["name"] != self.username: self.disconnect("Client's username did not" @@ -1334,8 +1330,8 @@ def _add_client(self): Put client into server data. (player login will be called later by mcserver.py) """ - if self not in self.proxy.srv_data.clients: - self.proxy.srv_data.clients.append(self) + if self not in self.proxy.clients: + self.proxy.clients.append(self) def _keep_alive_tracker(self): """ @@ -1386,10 +1382,10 @@ def _remove_client_and_player(self): onto the local server (which normally keeps tabs on player and client objects). """ - if self.username in self.proxy.srv_data.players: - if self.proxy.srv_data.players[self.username].client.state != LOBBY: - self.proxy.srv_data.players[self.username].abort = True - del self.proxy.srv_data.players[self.username] + if self.username in self.proxy.wrapper.players: + if self.proxy.wrapper.players[self.username].client.state != LOBBY: + self.proxy.wrapper.players[self.username].abort = True + del self.proxy.wrapper.players[self.username] def send_client_settings(self): """ diff --git a/wrapper/proxy/client/parse_sb.py b/wrapper/proxy/client/parse_sb.py index 0d8cc9c9..5e9ee982 100644 --- a/wrapper/proxy/client/parse_sb.py +++ b/wrapper/proxy/client/parse_sb.py @@ -10,7 +10,6 @@ import time from proxy.utils.constants import * -from proxy.utils.mcuuid import MCUUID # noinspection PyMethodMayBeStatic @@ -26,7 +25,7 @@ def __init__(self, client, packet): self.pktSB = self.client.pktSB self.pktCB = self.client.pktCB - self.command_prefix = self.proxy.srv_data.command_prefix + self.command_prefix = self.proxy.command_prefix self.command_prefix_non_standard = self.command_prefix != "/" def keep_alive(self): @@ -78,12 +77,14 @@ def _plugin_response(self, response): """ if "ip" in response: self.client.info["username"] = response["username"] - self.client.info["realuuid"] = MCUUID(response["realuuid"]).string + self.client.info["realuuid"] = self.proxy.wrapper.mcuuid( + response["realuuid"]).string self.client.info["ip"] = response["ip"] self.client.ip = response["ip"] if response["realuuid"] != "": - self.client.mojanguuid = MCUUID(response["realuuid"]) + self.client.mojanguuid = self.proxy.wrapper.mcuuid( + response["realuuid"]) self.client.username = response["username"] return True else: @@ -173,7 +174,7 @@ def play_chat_message(self): return True try: - player = self.client.srv_data.players[self.client.username] + player = self.client.proxy.wrapper.players[self.client.username] except KeyError: return False payload = self.proxy.eventhandler.callevent("player.rawMessage", { @@ -228,7 +229,7 @@ def play_chat_message(self): "playername": self.client.username, "player": - self.client.srv_data.players[self.client.username], + self.client.proxy.wrapper.players[self.client.username], "command": allwords[0][1:], "args": @@ -287,8 +288,8 @@ def _world_hub_command(self, where=""): return self._world_hub_help("w") elif where == "": - port = self.proxy.srv_data.server_port - ip = "127.0.0.1" + port = self.client.javaserver.server_port + ip = "localhost" else: worlds = self.proxy.proxy_worlds if where in worlds: @@ -296,7 +297,7 @@ def _world_hub_command(self, where=""): try: ip = self.proxy.proxy_worlds[where]["ip"] except KeyError: - ip = "127.0.0.1" + ip = "localhost" else: return self._world_hub_help("w") t = threading.Thread(target=self.client.change_servers, @@ -373,7 +374,7 @@ def play_player_digging(self): position = data[1] try: - player = self.client.srv_data.players[self.client.username] + player = self.client.proxy.wrapper.players[self.client.username] except KeyError: return False # finished digging @@ -419,7 +420,7 @@ def play_player_digging(self): if self.client.gamemode != 1: if not self.proxy.eventhandler.callevent("player.dig", { "playername": self.client.username, - "player": self.client.srv_data.players[ + "player": self.client.proxy.wrapper.players[ self.client.username], "position": position, "action": "begin_break", @@ -429,7 +430,7 @@ def play_player_digging(self): else: if not self.proxy.eventhandler.callevent("player.dig", { "playername": self.client.username, - "player": self.client.srv_data.players[ + "player": self.client.proxy.wrapper.players[ self.client.username], "position": position, "action": "end_break", @@ -536,7 +537,7 @@ def play_player_block_placement(self): position = (position[0] + 1, position[1], position[2]) try: - player = self.client.srv_data.players[self.client.username] + player = self.client.proxy.wrapper.players[self.client.username] except KeyError: return False @@ -594,7 +595,7 @@ def play_use_item(self): # no 1.8 or prior packet position = self.client.lastplacecoords[0] try: - player = self.client.srv_data.players[self.client.username] + player = self.client.proxy.wrapper.players[self.client.username] except KeyError: return False if not self.proxy.eventhandler.callevent("player.interact", { @@ -643,7 +644,7 @@ def play_player_update_sign(self): l4 = data[6] try: - player = self.client.srv_data.players[self.client.username] + player = self.client.proxy.wrapper.players[self.client.username] except KeyError: return False payload = self.proxy.eventhandler.callevent("player.createSign", { @@ -734,7 +735,7 @@ def play_click_window(self): # click window data = [False, 0, 0, 0, 0, 0, 0] try: - player = self.client.srv_data.players[self.client.username] + player = self.client.proxy.wrapper.players[self.client.username] except KeyError: return False datadict = { diff --git a/wrapper/proxy/entity/entitycontrol.py b/wrapper/proxy/entity/entitycontrol.py index cb5932ba..b3a46969 100644 --- a/wrapper/proxy/entity/entitycontrol.py +++ b/wrapper/proxy/entity/entitycontrol.py @@ -39,11 +39,11 @@ def __init__(self, proxy): self.proxy = proxy self.ent_config = self.proxy.ent_config - self.srvr_data = self.proxy.srv_data + self.javaserver = self.proxy.wrapper.javaserver self._log = self.proxy.log # Entities - living beings (includes XP orbs!) - pre1_11 = self.srvr_data.version_compute < 11100 + pre1_11 = self.javaserver.version_compute < 11100 entitylistobject = Entitytypes(pre1_11) self.entitytypes = entitylistobject.entitylist @@ -221,8 +221,8 @@ def _entity_processor(self): self._log.debug("_entityprocessor thread started.") timer = float(0) # server is running - while self.srvr_data.state in (1, 2, 4) and not ( - self.proxy.caller.halt or self.proxy.abort + while self.javaserver.state in (1, 2, 4) and not ( + self.proxy.wrapper.haltsig.halt or self.proxy.abort ): timer += .1 sleep(.1) @@ -234,7 +234,7 @@ def _entity_processor(self): # start looking for stale client entities playerlist = [] - for player in self.srvr_data.clients: + for player in self.proxy.clients: playerlist.append(player.username) entity_eids = list(self.entities.keys()) for eid in entity_eids: @@ -253,8 +253,8 @@ def _entity_thinner(self): timer = float(0) # while server is running - while self.srvr_data.state in (1, 2, 4) and not ( - self.proxy.caller.halt or self.proxy.abort + while self.javaserver.state in (1, 2, 4) and not ( + self.proxy.wrapper.haltsig.halt or self.proxy.abort ): timer += .1 @@ -270,7 +270,7 @@ def _entity_thinner(self): continue # gather client list - playerlist = self.srvr_data.clients + playerlist = self.proxy.clients # loop through playerlist for playerclient in playerlist: players_position = playerclient.position @@ -294,8 +294,8 @@ def _entity_thinner(self): # turn off console_spam server_msg = "Teleported %s to" % mob_type - if server_msg not in self.srvr_data.spammy_stuff: - self.srvr_data.spammy_stuff.append( + if server_msg not in self.javaserver.spammy_stuff: + self.javaserver.spammy_stuff.append( "Teleported %s to" % mob_type) # can't be too agressive with killing because @@ -313,8 +313,6 @@ def _kill_around_player(self, position, entity_name, count): pos = position # send those creatures away self._log.debug("killing %d %s" % (count, entity_name)) - - # if self.proxy.srv_data.protocolVersion < 204: console_command = "tp @e[type=%s,x=%d,y=%d,z=%d,c=%s] ~ ~-500 ~" % ( entity_name, pos[0], pos[1], pos[2], count) self.proxy.run_command(console_command) diff --git a/wrapper/proxy/packets/packet.py b/wrapper/proxy/packets/packet.py index e7b7918b..0133737f 100644 --- a/wrapper/proxy/packets/packet.py +++ b/wrapper/proxy/packets/packet.py @@ -18,7 +18,6 @@ # import StringIO # local -from proxy.utils.mcuuid import MCUUID from proxy.utils.constants import * # Py3-2 @@ -77,7 +76,7 @@ def __init__(self, sock, obj): # this is set by the calling class/method. Not presently used here, # but could be. maybe to decide which metadata parser to use? - self.version = self.obj.srv_data.protocolVersion + self.version = self.obj.javaserver.protocolVersion self.buffer = io.BytesIO() # Py3 # self.buffer = StringIO.StringIO() @@ -719,7 +718,7 @@ def read_position(self): return x, y, z def read_uuid(self): - return MCUUID(bytes=self.read_data(16)) + return self.obj.proxy.wrapper.mcuuid(bytes=self.read_data(16)) def read_metadata_1_9(self): meta_data = {} diff --git a/wrapper/proxy/server/parse_cb.py b/wrapper/proxy/server/parse_cb.py index de0cb122..011e7f6a 100644 --- a/wrapper/proxy/server/parse_cb.py +++ b/wrapper/proxy/server/parse_cb.py @@ -220,7 +220,7 @@ def play_chat_message(self): # self.log.debug(data) try: - player = self.client.srv_data.players[self.client.username] + player = self.proxy.wrapper.players[self.client.username] except KeyError: return False payload = self.proxy.eventhandler.callevent( @@ -284,7 +284,7 @@ def play_use_bed(self): data = self.packet.readpkt([VARINT, POSITION]) try: - player = self.client.srv_data.players[self.client.username] + player = self.proxy.wrapper.players[self.client.username] except KeyError: return False if data[0] == self.client.server_eid: @@ -336,7 +336,7 @@ def play_spawn_position(self): data = self.packet.readpkt([POSITION]) try: - player = self.client.srv_data.players[self.client.username] + player = self.proxy.wrapper.players[self.client.username] except KeyError: return False self.proxy.eventhandler.callevent( @@ -409,7 +409,7 @@ def play_time_update(self): # There could be a number of clients trying to update this at once # noinspection PyBroadException try: - self.proxy.srv_data.timeofday = data[1] + self.proxy.wrapper.javaserver.timeofday = data[1] except: pass return True @@ -441,7 +441,7 @@ def play_tab_complete(self): print([match]) try: - player = self.client.srv_data.players[self.client.username] + player = self.proxy.wrapper.players[self.client.username] except KeyError: return False payload = self.proxy.eventhandler.callevent( @@ -682,7 +682,7 @@ def play_attach_entity(self): if entityeid == self.client.server_eid: try: - player = self.client.srv_data.players[self.client.username] + player = self.proxy.wrapper.players[self.client.username] except KeyError: return False if not leash: diff --git a/wrapper/proxy/server/serverconnection.py b/wrapper/proxy/server/serverconnection.py index 27fcd62e..37a18a42 100644 --- a/wrapper/proxy/server/serverconnection.py +++ b/wrapper/proxy/server/serverconnection.py @@ -19,7 +19,6 @@ from proxy.packets import mcpackets_cb from proxy.utils.constants import * -from proxy.utils.mcuuid import MCUUID # noinspection PyMethodMayBeStatic,PyBroadException @@ -43,10 +42,11 @@ def __init__(self, client, ip=None, port=None): self.client = client self.username = self.client.username self.proxy = client.proxy + self.wrapper = self.proxy.wrapper + self.javaserver = self.wrapper.javaserver self.log = client.log self.ip = ip self.port = port - self.srv_data = self.proxy.srv_data # server setup and operating paramenters self.abort = False @@ -72,7 +72,7 @@ def __init__(self, client, ip=None, port=None): def _refresh_server_version(self): """Get serverversion for mcpackets use""" - self.version = self.proxy.srv_data.protocolVersion + self.version = self.proxy.javaserver.protocolVersion self.pktSB = mcpackets_sb.Packets(self.version) self.pktCB = mcpackets_cb.Packets(self.version) self.parse_cb = ParseCB(self, self.packet) @@ -89,7 +89,7 @@ def connect(self): # Connect to a local server address if self.ip is None: self.server_socket.connect(( - "localhost", self.proxy.srv_data.server_port)) + "localhost", self.proxy.javaserver.server_port)) # Connect to some specific server address else: @@ -167,7 +167,7 @@ def close_server(self, reason="Disconnected"): return False self.log.info("%s's proxy server connection closed: %s", - self.username, reason) + self.username, reason) # end 'handle' and 'flush_loop' cleanly self.abort = True @@ -176,6 +176,7 @@ def close_server(self, reason="Disconnected"): # noinspection PyBroadException try: self.server_socket.shutdown(2) + self.server_socket.close() self.log.debug("Sucessfully closed server socket for" " %s", self.username) # allow old packet and socket to be Garbage Collected @@ -260,13 +261,9 @@ def _parse_login_encr_request(self): return False # Login Success - UUID & Username are sent in this packet as strings + # no point in parsing because we already know the UUID and Username def _parse_login_success(self): self.state = PLAY - # todo - we may not need to assign this to a variable. - # (we supplied uuid/name anyway!) - # noinspection PyUnusedLocal - data = self.packet.readpkt([STRING, STRING]) - self.client.local_uuid = MCUUID(data[0]) return False def _parse_login_set_compression(self):