From e63a85e4a46b03d90962194f8aaed5cd4bfc931f Mon Sep 17 00:00:00 2001 From: Giuseppe Guerra Date: Thu, 21 Dec 2017 18:58:56 +0100 Subject: [PATCH] I knew a threaded bancho server was going to be a bad idea... --- events/changeMatchModsEvent.py | 46 +++---- events/changeMatchPasswordEvent.py | 14 +-- events/changeMatchSettingsEvent.py | 140 +++++++++++----------- events/changeSlotEvent.py | 8 +- events/createMatchEvent.py | 16 ++- events/joinMatchEvent.py | 14 +-- events/matchBeatmapEvent.py | 6 +- events/matchChangeTeamEvent.py | 6 +- events/matchCompleteEvent.py | 6 +- events/matchFailedEvent.py | 6 +- events/matchFramesEvent.py | 20 ++-- events/matchInviteEvent.py | 6 +- events/matchLockEvent.py | 20 ++-- events/matchPlayerLoadEvent.py | 6 +- events/matchReadyEvent.py | 18 +-- events/matchSkipEvent.py | 6 +- events/matchStartEvent.py | 12 +- events/matchTransferHostEvent.py | 12 +- events/tournamentMatchInfoRequestEvent.py | 3 +- objects/match.py | 54 +++++---- 20 files changed, 200 insertions(+), 219 deletions(-) diff --git a/events/changeMatchModsEvent.py b/events/changeMatchModsEvent.py index 3b9f107..58aa837 100644 --- a/events/changeMatchModsEvent.py +++ b/events/changeMatchModsEvent.py @@ -15,29 +15,29 @@ def handle(userToken, packetData): matchID = userToken.matchID if matchID not in glob.matches.matches: return - match = glob.matches.matches[matchID] # Set slot or match mods according to modType - if match.matchModMode == matchModModes.FREE_MOD: - # Freemod - # Host can set global DT/HT - if userID == match.hostUserID: - # If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod - if (packetData["mods"] & mods.DOUBLETIME) > 0: - match.changeMods(mods.DOUBLETIME) - # Nightcore - if (packetData["mods"] & mods.NIGHTCORE) > 0: - match.changeMods(match.mods + mods.NIGHTCORE) - elif (packetData["mods"] & mods.HALFTIME) > 0: - match.changeMods(mods.HALFTIME) - else: - # No DT/HT, set global mods to 0 (we are in freemod mode) - match.changeMods(0) + with glob.matches.matches[matchID] as match: + if match.matchModMode == matchModModes.FREE_MOD: + # Freemod + # Host can set global DT/HT + if userID == match.hostUserID: + # If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod + if (packetData["mods"] & mods.DOUBLETIME) > 0: + match.changeMods(mods.DOUBLETIME) + # Nightcore + if (packetData["mods"] & mods.NIGHTCORE) > 0: + match.changeMods(match.mods + mods.NIGHTCORE) + elif (packetData["mods"] & mods.HALFTIME) > 0: + match.changeMods(mods.HALFTIME) + else: + # No DT/HT, set global mods to 0 (we are in freemod mode) + match.changeMods(0) - # Set slot mods - slotID = match.getUserSlotID(userID) - if slotID is not None: - match.setSlotMods(slotID, packetData["mods"]) - else: - # Not freemod, set match mods - match.changeMods(packetData["mods"]) + # Set slot mods + slotID = match.getUserSlotID(userID) + if slotID is not None: + match.setSlotMods(slotID, packetData["mods"]) + else: + # Not freemod, set match mods + match.changeMods(packetData["mods"]) diff --git a/events/changeMatchPasswordEvent.py b/events/changeMatchPasswordEvent.py index 1c645b7..24cae23 100644 --- a/events/changeMatchPasswordEvent.py +++ b/events/changeMatchPasswordEvent.py @@ -10,12 +10,10 @@ def handle(userToken, packetData): if matchID not in glob.matches.matches: return - # Get our match - match = glob.matches.matches[matchID] + with glob.matches.matches[matchID] as match: + # Host check + if userToken.userID != match.hostUserID: + return - # Host check - if userToken.userID != match.hostUserID: - return - - # Update match password - match.changePassword(packetData["matchPassword"]) + # Update match password + match.changePassword(packetData["matchPassword"]) diff --git a/events/changeMatchSettingsEvent.py b/events/changeMatchSettingsEvent.py index 8581949..ac8d241 100644 --- a/events/changeMatchSettingsEvent.py +++ b/events/changeMatchSettingsEvent.py @@ -21,86 +21,84 @@ def handle(userToken, packetData): if matchID not in glob.matches.matches: return - # Get match object - match = glob.matches.matches[matchID] - # Host check - if userToken.userID != match.hostUserID: - return + with glob.matches.matches[matchID] as match: + if userToken.userID != match.hostUserID: + return - # Some dank memes easter egg - memeTitles = [ - "RWC 2020", - "Fokabot is a duck", - "Dank memes", - "1337ms Ping", - "Iscriviti a Xenotoze", - "...e i marò?", - "Superman dies", - "The brace is on fire", - "print_foot()", - "#FREEZEBARKEZ", - "Ripple devs are actually cats", - "Thank Mr Shaural", - "NEVER GIVE UP", - "T I E D W I T H U N I T E D", - "HIGHEST HDHR LOBBY OF ALL TIME", - "This is gasoline and I set myself on fire", - "Everyone is cheating apparently", - "Kurwa mac", - "TATOE", - "This is not your drama landfill.", - "I like cheese", - "NYO IS NOT A CAT HE IS A DO(N)G", - "Datingu startuato" - ] + # Some dank memes easter egg + memeTitles = [ + "RWC 2020", + "Fokabot is a duck", + "Dank memes", + "1337ms Ping", + "Iscriviti a Xenotoze", + "...e i marò?", + "Superman dies", + "The brace is on fire", + "print_foot()", + "#FREEZEBARKEZ", + "Ripple devs are actually cats", + "Thank Mr Shaural", + "NEVER GIVE UP", + "T I E D W I T H U N I T E D", + "HIGHEST HDHR LOBBY OF ALL TIME", + "This is gasoline and I set myself on fire", + "Everyone is cheating apparently", + "Kurwa mac", + "TATOE", + "This is not your drama landfill.", + "I like cheese", + "NYO IS NOT A CAT HE IS A DO(N)G", + "Datingu startuato" + ] - # Set match name - match.matchName = packetData["matchName"] if packetData["matchName"] != "meme" else random.choice(memeTitles) + # Set match name + match.matchName = packetData["matchName"] if packetData["matchName"] != "meme" else random.choice(memeTitles) - # Update match settings - match.inProgress = packetData["inProgress"] - if packetData["matchPassword"] != "": - match.matchPassword = generalUtils.stringMd5(packetData["matchPassword"]) - else: - match.matchPassword = "" - match.beatmapName = packetData["beatmapName"] - match.beatmapID = packetData["beatmapID"] - match.hostUserID = packetData["hostUserID"] - match.gameMode = packetData["gameMode"] + # Update match settings + match.inProgress = packetData["inProgress"] + if packetData["matchPassword"] != "": + match.matchPassword = generalUtils.stringMd5(packetData["matchPassword"]) + else: + match.matchPassword = "" + match.beatmapName = packetData["beatmapName"] + match.beatmapID = packetData["beatmapID"] + match.hostUserID = packetData["hostUserID"] + match.gameMode = packetData["gameMode"] - oldBeatmapMD5 = match.beatmapMD5 - oldMods = match.mods - oldMatchTeamType = match.matchTeamType + oldBeatmapMD5 = match.beatmapMD5 + oldMods = match.mods + oldMatchTeamType = match.matchTeamType - match.mods = packetData["mods"] - match.beatmapMD5 = packetData["beatmapMD5"] - match.matchScoringType = packetData["scoringType"] - match.matchTeamType = packetData["teamType"] - match.matchModMode = packetData["freeMods"] + match.mods = packetData["mods"] + match.beatmapMD5 = packetData["beatmapMD5"] + match.matchScoringType = packetData["scoringType"] + match.matchTeamType = packetData["teamType"] + match.matchModMode = packetData["freeMods"] - # Reset ready if needed - if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5: - match.resetReady() + # Reset ready if needed + if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5: + match.resetReady() - # Reset mods if needed - if match.matchModMode == matchModModes.NORMAL: - # Reset slot mods if not freeMods - match.resetMods() - else: - # Reset match mods if freemod - match.mods = 0 + # Reset mods if needed + if match.matchModMode == matchModModes.NORMAL: + # Reset slot mods if not freeMods + match.resetMods() + else: + # Reset match mods if freemod + match.mods = 0 - # Initialize teams if team type changed - if match.matchTeamType != oldMatchTeamType: - match.initializeTeams() + # Initialize teams if team type changed + if match.matchTeamType != oldMatchTeamType: + match.initializeTeams() - # Force no freemods if tag coop - if match.matchTeamType == matchTeamTypes.TAG_COOP or match.matchTeamType == matchTeamTypes.TAG_TEAM_VS: - match.matchModMode = matchModModes.NORMAL + # Force no freemods if tag coop + if match.matchTeamType == matchTeamTypes.TAG_COOP or match.matchTeamType == matchTeamTypes.TAG_TEAM_VS: + match.matchModMode = matchModModes.NORMAL - # Send updated settings - match.sendUpdates() + # Send updated settings + match.sendUpdates() - # Console output - log.info("MPROOM{}: Updated room settings".format(match.matchID)) + # Console output + log.info("MPROOM{}: Updated room settings".format(match.matchID)) diff --git a/events/changeSlotEvent.py b/events/changeSlotEvent.py index 5f2d30b..030cbeb 100644 --- a/events/changeSlotEvent.py +++ b/events/changeSlotEvent.py @@ -8,8 +8,6 @@ def handle(userToken, packetData): # Read packet data packetData = clientPackets.changeSlot(packetData) - # Get match - match = glob.matches.matches[userToken.matchID] - - # Change slot - match.userChangeSlot(userID, packetData["slotID"]) + with glob.matches.matches[userToken.matchID] as match: + # Change slot + match.userChangeSlot(userID, packetData["slotID"]) diff --git a/events/createMatchEvent.py b/events/createMatchEvent.py index 00f3dca..2973842 100644 --- a/events/createMatchEvent.py +++ b/events/createMatchEvent.py @@ -20,15 +20,13 @@ def handle(userToken, packetData): if matchID not in glob.matches.matches: raise exceptions.matchCreateError() - # Get match object - match = glob.matches.matches[matchID] + with glob.matches.matches[matchID] as match: + # Join that match + userToken.joinMatch(matchID) - # Join that match - userToken.joinMatch(matchID) - - # Give host to match creator - match.setHost(userID) - match.sendUpdates() - match.changePassword(packetData["matchPassword"]) + # Give host to match creator + match.setHost(userID) + match.sendUpdates() + match.changePassword(packetData["matchPassword"]) except exceptions.matchCreateError: log.error("Error while creating match!") diff --git a/events/joinMatchEvent.py b/events/joinMatchEvent.py index 4704700..e3728b5 100644 --- a/events/joinMatchEvent.py +++ b/events/joinMatchEvent.py @@ -17,19 +17,17 @@ def handle(userToken, packetData): if matchID not in glob.matches.matches: return - # Match exists, get object - match = glob.matches.matches[matchID] - # Hash password if needed - #if password != "": + # if password != "": # password = generalUtils.stringMd5(password) # Check password - if match.matchPassword != "" and match.matchPassword != password: - raise exceptions.matchWrongPasswordException() + with glob.matches.matches[matchID] as match: + if match.matchPassword != "" and match.matchPassword != password: + raise exceptions.matchWrongPasswordException() - # Password is correct, join match - userToken.joinMatch(matchID) + # Password is correct, join match + userToken.joinMatch(matchID) except exceptions.matchWrongPasswordException: userToken.enqueue(serverPackets.matchJoinFail()) log.warning("{} has tried to join a mp room, but he typed the wrong password".format(userToken.username)) \ No newline at end of file diff --git a/events/matchBeatmapEvent.py b/events/matchBeatmapEvent.py index e10140d..dabf168 100644 --- a/events/matchBeatmapEvent.py +++ b/events/matchBeatmapEvent.py @@ -15,8 +15,6 @@ def handle(userToken, _, has): if matchID not in glob.matches.matches: return - # The match exists, get object - match = glob.matches.matches[matchID] - # Set has beatmap/no beatmap - match.userHasBeatmap(userID, has) + with glob.matches.matches[matchID] as match: + match.userHasBeatmap(userID, has) diff --git a/events/matchChangeTeamEvent.py b/events/matchChangeTeamEvent.py index 010bc8e..46ab7eb 100644 --- a/events/matchChangeTeamEvent.py +++ b/events/matchChangeTeamEvent.py @@ -15,8 +15,6 @@ def handle(userToken, _): if matchID not in glob.matches.matches: return - # Get match object - match = glob.matches.matches[matchID] - # Change team - match.changeTeam(userID) + with glob.matches.matches[matchID] as match: + match.changeTeam(userID) diff --git a/events/matchCompleteEvent.py b/events/matchCompleteEvent.py index fc6adf2..02acbeb 100644 --- a/events/matchCompleteEvent.py +++ b/events/matchCompleteEvent.py @@ -15,8 +15,6 @@ def handle(userToken, _): if matchID not in glob.matches.matches: return - # The match exists, get object - match = glob.matches.matches[matchID] - # Set our match complete - match.playerCompleted(userID) + with glob.matches.matches[matchID] as match: + match.playerCompleted(userID) diff --git a/events/matchFailedEvent.py b/events/matchFailedEvent.py index d9b01c7..93b3191 100644 --- a/events/matchFailedEvent.py +++ b/events/matchFailedEvent.py @@ -15,8 +15,6 @@ def handle(userToken, _): if matchID not in glob.matches.matches: return - # Match exists, get object - match = glob.matches.matches[matchID] - # Fail user - match.playerFailed(userID) + with glob.matches.matches[matchID] as match: + match.playerFailed(userID) diff --git a/events/matchFramesEvent.py b/events/matchFramesEvent.py index 41c176c..8f0861f 100644 --- a/events/matchFramesEvent.py +++ b/events/matchFramesEvent.py @@ -16,18 +16,16 @@ def handle(userToken, packetData): if matchID not in glob.matches.matches: return - # The match exists, get object - match = glob.matches.matches[matchID] - - # Change slot id in packetData - slotID = match.getUserSlotID(userID) - # Parse the data data = clientPackets.matchFrames(packetData) - # Update the score - match.updateScore(slotID, data["totalScore"]) - match.updateHP(slotID, data["currentHp"]) + with glob.matches.matches[matchID] as match: + # Change slot id in packetData + slotID = match.getUserSlotID(userID) - # Enqueue frames to who's playing - glob.streams.broadcast(match.playingStreamName, serverPackets.matchFrames(slotID, packetData)) \ No newline at end of file + # Update the score + match.updateScore(slotID, data["totalScore"]) + match.updateHP(slotID, data["currentHp"]) + + # Enqueue frames to who's playing + glob.streams.broadcast(match.playingStreamName, serverPackets.matchFrames(slotID, packetData)) \ No newline at end of file diff --git a/events/matchInviteEvent.py b/events/matchInviteEvent.py index 532b943..affc6e1 100644 --- a/events/matchInviteEvent.py +++ b/events/matchInviteEvent.py @@ -17,8 +17,6 @@ def handle(userToken, packetData): if matchID not in glob.matches.matches: return - # Get match object - match = glob.matches.matches[matchID] - # Send invite - match.invite(userID, packetData["userID"]) + with glob.matches.matches[matchID] as match: + match.invite(userID, packetData["userID"]) diff --git a/events/matchLockEvent.py b/events/matchLockEvent.py index 553db86..bae5f40 100644 --- a/events/matchLockEvent.py +++ b/events/matchLockEvent.py @@ -12,16 +12,16 @@ def handle(userToken, packetData): matchID = userToken.matchID if matchID not in glob.matches.matches: return - match = glob.matches.matches[matchID] - # Host check - if userID != match.hostUserID: - return + with glob.matches.matches[matchID] as match: + # Host check + if userID != match.hostUserID: + return - # Make sure we aren't locking our slot - ourSlot = match.getUserSlotID(userID) - if packetData["slotID"] == ourSlot: - return + # Make sure we aren't locking our slot + ourSlot = match.getUserSlotID(userID) + if packetData["slotID"] == ourSlot: + return - # Lock/Unlock slot - match.toggleSlotLocked(packetData["slotID"]) + # Lock/Unlock slot + match.toggleSlotLocked(packetData["slotID"]) diff --git a/events/matchPlayerLoadEvent.py b/events/matchPlayerLoadEvent.py index a40a099..0ca47d8 100644 --- a/events/matchPlayerLoadEvent.py +++ b/events/matchPlayerLoadEvent.py @@ -15,8 +15,6 @@ def handle(userToken, _): if matchID not in glob.matches.matches: return - # The match exists, get object - match = glob.matches.matches[matchID] - # Set our load status - match.playerLoaded(userID) + with glob.matches.matches[matchID] as match: + match.playerLoaded(userID) diff --git a/events/matchReadyEvent.py b/events/matchReadyEvent.py index 05e731e..2a2a974 100644 --- a/events/matchReadyEvent.py +++ b/events/matchReadyEvent.py @@ -8,14 +8,14 @@ def handle(userToken, _): matchID = userToken.matchID if matchID not in glob.matches.matches: return - match = glob.matches.matches[matchID] - # Get our slotID and change ready status - slotID = match.getUserSlotID(userID) - if slotID is not None: - match.toggleSlotReady(slotID) + with glob.matches.matches[matchID] as match: + # Get our slotID and change ready status + slotID = match.getUserSlotID(userID) + if slotID is not None: + match.toggleSlotReady(slotID) - # If this is a tournament match, we should send the current status of ready - # players. - if match.isTourney: - match.sendReadyStatus() + # If this is a tournament match, we should send the current status of ready + # players. + if match.isTourney: + match.sendReadyStatus() diff --git a/events/matchSkipEvent.py b/events/matchSkipEvent.py index cbfc592..70aa449 100644 --- a/events/matchSkipEvent.py +++ b/events/matchSkipEvent.py @@ -15,8 +15,6 @@ def handle(userToken, _): if matchID not in glob.matches.matches: return - # The match exists, get object - match = glob.matches.matches[matchID] - # Skip - match.playerSkip(userID) + with glob.matches.matches[matchID] as match: + match.playerSkip(userID) diff --git a/events/matchStartEvent.py b/events/matchStartEvent.py index da83b37..46476e9 100644 --- a/events/matchStartEvent.py +++ b/events/matchStartEvent.py @@ -13,11 +13,9 @@ def handle(userToken, _): if matchID not in glob.matches.matches: return - # The match exists, get object - match = glob.matches.matches[matchID] + with glob.matches.matches[matchID] as match: + # Host check + if userToken.userID != match.hostUserID: + return - # Host check - if userToken.userID != match.hostUserID: - return - - match.start() + match.start() diff --git a/events/matchTransferHostEvent.py b/events/matchTransferHostEvent.py index b6a9432..d646375 100644 --- a/events/matchTransferHostEvent.py +++ b/events/matchTransferHostEvent.py @@ -16,12 +16,10 @@ def handle(userToken, packetData): if matchID not in glob.matches.matches: return - # Match exists, get object - match = glob.matches.matches[matchID] - # Host check - if userToken.userID != match.hostUserID: - return + with glob.matches.matches[matchID] as match: + if userToken.userID != match.hostUserID: + return - # Transfer host - match.transferHost(packetData["slotID"]) + # Transfer host + match.transferHost(packetData["slotID"]) diff --git a/events/tournamentMatchInfoRequestEvent.py b/events/tournamentMatchInfoRequestEvent.py index 43011ea..2d1ef0e 100644 --- a/events/tournamentMatchInfoRequestEvent.py +++ b/events/tournamentMatchInfoRequestEvent.py @@ -6,4 +6,5 @@ def handle(userToken, packetData): matchID = packetData["matchID"] if matchID not in glob.matches.matches or not userToken.tournament: return - userToken.enqueue(glob.matches.matches[matchID].matchDataCache) \ No newline at end of file + with glob.matches.matches[matchID] as m: + userToken.enqueue(m.matchDataCache) \ No newline at end of file diff --git a/objects/match.py b/objects/match.py index da9c496..5f1f271 100644 --- a/objects/match.py +++ b/objects/match.py @@ -1,5 +1,6 @@ import copy import json +import threading from common.log import logUtils as log from constants import dataTypes from constants import matchModModes @@ -60,6 +61,7 @@ class match: self.isTourney = isTourney self.isLocked = False # if True, users can't change slots/teams. Used in tourney matches self.isStarting = False + self._lock = threading.Lock() # Create all slots and reset them self.slots = [] @@ -83,55 +85,55 @@ class match: """ # General match info # TODO: Test without safe copy, the error might have been caused by outdated python bytecode cache - safeMatch = copy.deepcopy(self) + # safeMatch = copy.deepcopy(self) struct = [ - [safeMatch.matchID, dataTypes.UINT16], - [int(safeMatch.inProgress), dataTypes.BYTE], + [self.matchID, dataTypes.UINT16], + [int(self.inProgress), dataTypes.BYTE], [0, dataTypes.BYTE], - [safeMatch.mods, dataTypes.UINT32], - [safeMatch.matchName, dataTypes.STRING] + [self.mods, dataTypes.UINT32], + [self.matchName, dataTypes.STRING] ] - if censored and safeMatch.matchPassword: + if censored and self.matchPassword: struct.append(["redacted", dataTypes.STRING]) else: - struct.append([safeMatch.matchPassword, dataTypes.STRING]) + struct.append([self.matchPassword, dataTypes.STRING]) struct.extend([ - [safeMatch.beatmapName, dataTypes.STRING], - [safeMatch.beatmapID, dataTypes.UINT32], - [safeMatch.beatmapMD5, dataTypes.STRING] + [self.beatmapName, dataTypes.STRING], + [self.beatmapID, dataTypes.UINT32], + [self.beatmapMD5, dataTypes.STRING] ]) # Slots status IDs, always 16 elements for i in range(0,16): - struct.append([safeMatch.slots[i].status, dataTypes.BYTE]) + struct.append([self.slots[i].status, dataTypes.BYTE]) # Slot teams, always 16 elements for i in range(0,16): - struct.append([safeMatch.slots[i].team, dataTypes.BYTE]) + struct.append([self.slots[i].team, dataTypes.BYTE]) # Slot user ID. Write only if slot is occupied for i in range(0,16): - if safeMatch.slots[i].user is not None and safeMatch.slots[i].user in glob.tokens.tokens: - struct.append([glob.tokens.tokens[safeMatch.slots[i].user].userID, dataTypes.UINT32]) + if self.slots[i].user is not None and self.slots[i].user in glob.tokens.tokens: + struct.append([glob.tokens.tokens[self.slots[i].user].userID, dataTypes.UINT32]) # Other match data struct.extend([ - [safeMatch.hostUserID, dataTypes.SINT32], - [safeMatch.gameMode, dataTypes.BYTE], - [safeMatch.matchScoringType, dataTypes.BYTE], - [safeMatch.matchTeamType, dataTypes.BYTE], - [safeMatch.matchModMode, dataTypes.BYTE], + [self.hostUserID, dataTypes.SINT32], + [self.gameMode, dataTypes.BYTE], + [self.matchScoringType, dataTypes.BYTE], + [self.matchTeamType, dataTypes.BYTE], + [self.matchModMode, dataTypes.BYTE], ]) # Slot mods if free mod is enabled - if safeMatch.matchModMode == matchModModes.FREE_MOD: + if self.matchModMode == matchModModes.FREE_MOD: for i in range(0,16): - struct.append([safeMatch.slots[i].mods, dataTypes.UINT32]) + struct.append([self.slots[i].mods, dataTypes.UINT32]) # Seed idk # TODO: Implement this, it should be used for mania "random" mod - struct.append([safeMatch.seed, dataTypes.UINT32]) + struct.append([self.seed, dataTypes.UINT32]) return struct @@ -854,3 +856,11 @@ class match: message = "The match is now empty." chat.sendMessage("FokaBot", chanName, message) + + def __enter__(self): + # 🌚🌚🌚🌚🌚 + self._lock.acquire() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._lock.release()