I knew a threaded bancho server was going to be a bad idea...

This commit is contained in:
Giuseppe Guerra 2017-12-21 18:58:56 +01:00
parent 050c1d5fe8
commit e63a85e4a4
20 changed files with 200 additions and 219 deletions

View File

@ -15,29 +15,29 @@ def handle(userToken, packetData):
matchID = userToken.matchID matchID = userToken.matchID
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
match = glob.matches.matches[matchID]
# Set slot or match mods according to modType # Set slot or match mods according to modType
if match.matchModMode == matchModModes.FREE_MOD: with glob.matches.matches[matchID] as match:
# Freemod if match.matchModMode == matchModModes.FREE_MOD:
# Host can set global DT/HT # Freemod
if userID == match.hostUserID: # Host can set global DT/HT
# If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod if userID == match.hostUserID:
if (packetData["mods"] & mods.DOUBLETIME) > 0: # If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod
match.changeMods(mods.DOUBLETIME) if (packetData["mods"] & mods.DOUBLETIME) > 0:
# Nightcore match.changeMods(mods.DOUBLETIME)
if (packetData["mods"] & mods.NIGHTCORE) > 0: # Nightcore
match.changeMods(match.mods + mods.NIGHTCORE) if (packetData["mods"] & mods.NIGHTCORE) > 0:
elif (packetData["mods"] & mods.HALFTIME) > 0: match.changeMods(match.mods + mods.NIGHTCORE)
match.changeMods(mods.HALFTIME) elif (packetData["mods"] & mods.HALFTIME) > 0:
else: match.changeMods(mods.HALFTIME)
# No DT/HT, set global mods to 0 (we are in freemod mode) else:
match.changeMods(0) # No DT/HT, set global mods to 0 (we are in freemod mode)
match.changeMods(0)
# Set slot mods # Set slot mods
slotID = match.getUserSlotID(userID) slotID = match.getUserSlotID(userID)
if slotID is not None: if slotID is not None:
match.setSlotMods(slotID, packetData["mods"]) match.setSlotMods(slotID, packetData["mods"])
else: else:
# Not freemod, set match mods # Not freemod, set match mods
match.changeMods(packetData["mods"]) match.changeMods(packetData["mods"])

View File

@ -10,12 +10,10 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# Get our match with glob.matches.matches[matchID] as match:
match = glob.matches.matches[matchID] # Host check
if userToken.userID != match.hostUserID:
return
# Host check # Update match password
if userToken.userID != match.hostUserID: match.changePassword(packetData["matchPassword"])
return
# Update match password
match.changePassword(packetData["matchPassword"])

View File

@ -21,86 +21,84 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# Get match object
match = glob.matches.matches[matchID]
# Host check # Host check
if userToken.userID != match.hostUserID: with glob.matches.matches[matchID] as match:
return if userToken.userID != match.hostUserID:
return
# Some dank memes easter egg # Some dank memes easter egg
memeTitles = [ memeTitles = [
"RWC 2020", "RWC 2020",
"Fokabot is a duck", "Fokabot is a duck",
"Dank memes", "Dank memes",
"1337ms Ping", "1337ms Ping",
"Iscriviti a Xenotoze", "Iscriviti a Xenotoze",
"...e i marò?", "...e i marò?",
"Superman dies", "Superman dies",
"The brace is on fire", "The brace is on fire",
"print_foot()", "print_foot()",
"#FREEZEBARKEZ", "#FREEZEBARKEZ",
"Ripple devs are actually cats", "Ripple devs are actually cats",
"Thank Mr Shaural", "Thank Mr Shaural",
"NEVER GIVE UP", "NEVER GIVE UP",
"T I E D W I T H U N I T E D", "T I E D W I T H U N I T E D",
"HIGHEST HDHR LOBBY OF ALL TIME", "HIGHEST HDHR LOBBY OF ALL TIME",
"This is gasoline and I set myself on fire", "This is gasoline and I set myself on fire",
"Everyone is cheating apparently", "Everyone is cheating apparently",
"Kurwa mac", "Kurwa mac",
"TATOE", "TATOE",
"This is not your drama landfill.", "This is not your drama landfill.",
"I like cheese", "I like cheese",
"NYO IS NOT A CAT HE IS A DO(N)G", "NYO IS NOT A CAT HE IS A DO(N)G",
"Datingu startuato" "Datingu startuato"
] ]
# Set match name # Set match name
match.matchName = packetData["matchName"] if packetData["matchName"] != "meme" else random.choice(memeTitles) match.matchName = packetData["matchName"] if packetData["matchName"] != "meme" else random.choice(memeTitles)
# Update match settings # Update match settings
match.inProgress = packetData["inProgress"] match.inProgress = packetData["inProgress"]
if packetData["matchPassword"] != "": if packetData["matchPassword"] != "":
match.matchPassword = generalUtils.stringMd5(packetData["matchPassword"]) match.matchPassword = generalUtils.stringMd5(packetData["matchPassword"])
else: else:
match.matchPassword = "" match.matchPassword = ""
match.beatmapName = packetData["beatmapName"] match.beatmapName = packetData["beatmapName"]
match.beatmapID = packetData["beatmapID"] match.beatmapID = packetData["beatmapID"]
match.hostUserID = packetData["hostUserID"] match.hostUserID = packetData["hostUserID"]
match.gameMode = packetData["gameMode"] match.gameMode = packetData["gameMode"]
oldBeatmapMD5 = match.beatmapMD5 oldBeatmapMD5 = match.beatmapMD5
oldMods = match.mods oldMods = match.mods
oldMatchTeamType = match.matchTeamType oldMatchTeamType = match.matchTeamType
match.mods = packetData["mods"] match.mods = packetData["mods"]
match.beatmapMD5 = packetData["beatmapMD5"] match.beatmapMD5 = packetData["beatmapMD5"]
match.matchScoringType = packetData["scoringType"] match.matchScoringType = packetData["scoringType"]
match.matchTeamType = packetData["teamType"] match.matchTeamType = packetData["teamType"]
match.matchModMode = packetData["freeMods"] match.matchModMode = packetData["freeMods"]
# Reset ready if needed # Reset ready if needed
if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5: if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5:
match.resetReady() match.resetReady()
# Reset mods if needed # Reset mods if needed
if match.matchModMode == matchModModes.NORMAL: if match.matchModMode == matchModModes.NORMAL:
# Reset slot mods if not freeMods # Reset slot mods if not freeMods
match.resetMods() match.resetMods()
else: else:
# Reset match mods if freemod # Reset match mods if freemod
match.mods = 0 match.mods = 0
# Initialize teams if team type changed # Initialize teams if team type changed
if match.matchTeamType != oldMatchTeamType: if match.matchTeamType != oldMatchTeamType:
match.initializeTeams() match.initializeTeams()
# Force no freemods if tag coop # Force no freemods if tag coop
if match.matchTeamType == matchTeamTypes.TAG_COOP or match.matchTeamType == matchTeamTypes.TAG_TEAM_VS: if match.matchTeamType == matchTeamTypes.TAG_COOP or match.matchTeamType == matchTeamTypes.TAG_TEAM_VS:
match.matchModMode = matchModModes.NORMAL match.matchModMode = matchModModes.NORMAL
# Send updated settings # Send updated settings
match.sendUpdates() match.sendUpdates()
# Console output # Console output
log.info("MPROOM{}: Updated room settings".format(match.matchID)) log.info("MPROOM{}: Updated room settings".format(match.matchID))

View File

@ -8,8 +8,6 @@ def handle(userToken, packetData):
# Read packet data # Read packet data
packetData = clientPackets.changeSlot(packetData) packetData = clientPackets.changeSlot(packetData)
# Get match with glob.matches.matches[userToken.matchID] as match:
match = glob.matches.matches[userToken.matchID] # Change slot
match.userChangeSlot(userID, packetData["slotID"])
# Change slot
match.userChangeSlot(userID, packetData["slotID"])

View File

@ -20,15 +20,13 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
raise exceptions.matchCreateError() raise exceptions.matchCreateError()
# Get match object with glob.matches.matches[matchID] as match:
match = glob.matches.matches[matchID] # Join that match
userToken.joinMatch(matchID)
# Join that match # Give host to match creator
userToken.joinMatch(matchID) match.setHost(userID)
match.sendUpdates()
# Give host to match creator match.changePassword(packetData["matchPassword"])
match.setHost(userID)
match.sendUpdates()
match.changePassword(packetData["matchPassword"])
except exceptions.matchCreateError: except exceptions.matchCreateError:
log.error("Error while creating match!") log.error("Error while creating match!")

View File

@ -17,19 +17,17 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# Match exists, get object
match = glob.matches.matches[matchID]
# Hash password if needed # Hash password if needed
#if password != "": # if password != "":
# password = generalUtils.stringMd5(password) # password = generalUtils.stringMd5(password)
# Check password # Check password
if match.matchPassword != "" and match.matchPassword != password: with glob.matches.matches[matchID] as match:
raise exceptions.matchWrongPasswordException() if match.matchPassword != "" and match.matchPassword != password:
raise exceptions.matchWrongPasswordException()
# Password is correct, join match # Password is correct, join match
userToken.joinMatch(matchID) userToken.joinMatch(matchID)
except exceptions.matchWrongPasswordException: except exceptions.matchWrongPasswordException:
userToken.enqueue(serverPackets.matchJoinFail()) userToken.enqueue(serverPackets.matchJoinFail())
log.warning("{} has tried to join a mp room, but he typed the wrong password".format(userToken.username)) log.warning("{} has tried to join a mp room, but he typed the wrong password".format(userToken.username))

View File

@ -15,8 +15,6 @@ def handle(userToken, _, has):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# The match exists, get object
match = glob.matches.matches[matchID]
# Set has beatmap/no beatmap # Set has beatmap/no beatmap
match.userHasBeatmap(userID, has) with glob.matches.matches[matchID] as match:
match.userHasBeatmap(userID, has)

View File

@ -15,8 +15,6 @@ def handle(userToken, _):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# Get match object
match = glob.matches.matches[matchID]
# Change team # Change team
match.changeTeam(userID) with glob.matches.matches[matchID] as match:
match.changeTeam(userID)

View File

@ -15,8 +15,6 @@ def handle(userToken, _):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# The match exists, get object
match = glob.matches.matches[matchID]
# Set our match complete # Set our match complete
match.playerCompleted(userID) with glob.matches.matches[matchID] as match:
match.playerCompleted(userID)

View File

@ -15,8 +15,6 @@ def handle(userToken, _):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# Match exists, get object
match = glob.matches.matches[matchID]
# Fail user # Fail user
match.playerFailed(userID) with glob.matches.matches[matchID] as match:
match.playerFailed(userID)

View File

@ -16,18 +16,16 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# The match exists, get object
match = glob.matches.matches[matchID]
# Change slot id in packetData
slotID = match.getUserSlotID(userID)
# Parse the data # Parse the data
data = clientPackets.matchFrames(packetData) data = clientPackets.matchFrames(packetData)
# Update the score with glob.matches.matches[matchID] as match:
match.updateScore(slotID, data["totalScore"]) # Change slot id in packetData
match.updateHP(slotID, data["currentHp"]) slotID = match.getUserSlotID(userID)
# Enqueue frames to who's playing # Update the score
glob.streams.broadcast(match.playingStreamName, serverPackets.matchFrames(slotID, packetData)) 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))

View File

@ -17,8 +17,6 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# Get match object
match = glob.matches.matches[matchID]
# Send invite # Send invite
match.invite(userID, packetData["userID"]) with glob.matches.matches[matchID] as match:
match.invite(userID, packetData["userID"])

View File

@ -12,16 +12,16 @@ def handle(userToken, packetData):
matchID = userToken.matchID matchID = userToken.matchID
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
match = glob.matches.matches[matchID]
# Host check with glob.matches.matches[matchID] as match:
if userID != match.hostUserID: # Host check
return if userID != match.hostUserID:
return
# Make sure we aren't locking our slot # Make sure we aren't locking our slot
ourSlot = match.getUserSlotID(userID) ourSlot = match.getUserSlotID(userID)
if packetData["slotID"] == ourSlot: if packetData["slotID"] == ourSlot:
return return
# Lock/Unlock slot # Lock/Unlock slot
match.toggleSlotLocked(packetData["slotID"]) match.toggleSlotLocked(packetData["slotID"])

View File

@ -15,8 +15,6 @@ def handle(userToken, _):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# The match exists, get object
match = glob.matches.matches[matchID]
# Set our load status # Set our load status
match.playerLoaded(userID) with glob.matches.matches[matchID] as match:
match.playerLoaded(userID)

View File

@ -8,14 +8,14 @@ def handle(userToken, _):
matchID = userToken.matchID matchID = userToken.matchID
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
match = glob.matches.matches[matchID]
# Get our slotID and change ready status with glob.matches.matches[matchID] as match:
slotID = match.getUserSlotID(userID) # Get our slotID and change ready status
if slotID is not None: slotID = match.getUserSlotID(userID)
match.toggleSlotReady(slotID) if slotID is not None:
match.toggleSlotReady(slotID)
# If this is a tournament match, we should send the current status of ready # If this is a tournament match, we should send the current status of ready
# players. # players.
if match.isTourney: if match.isTourney:
match.sendReadyStatus() match.sendReadyStatus()

View File

@ -15,8 +15,6 @@ def handle(userToken, _):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# The match exists, get object
match = glob.matches.matches[matchID]
# Skip # Skip
match.playerSkip(userID) with glob.matches.matches[matchID] as match:
match.playerSkip(userID)

View File

@ -13,11 +13,9 @@ def handle(userToken, _):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# The match exists, get object with glob.matches.matches[matchID] as match:
match = glob.matches.matches[matchID] # Host check
if userToken.userID != match.hostUserID:
return
# Host check match.start()
if userToken.userID != match.hostUserID:
return
match.start()

View File

@ -16,12 +16,10 @@ def handle(userToken, packetData):
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
return return
# Match exists, get object
match = glob.matches.matches[matchID]
# Host check # Host check
if userToken.userID != match.hostUserID: with glob.matches.matches[matchID] as match:
return if userToken.userID != match.hostUserID:
return
# Transfer host # Transfer host
match.transferHost(packetData["slotID"]) match.transferHost(packetData["slotID"])

View File

@ -6,4 +6,5 @@ def handle(userToken, packetData):
matchID = packetData["matchID"] matchID = packetData["matchID"]
if matchID not in glob.matches.matches or not userToken.tournament: if matchID not in glob.matches.matches or not userToken.tournament:
return return
userToken.enqueue(glob.matches.matches[matchID].matchDataCache) with glob.matches.matches[matchID] as m:
userToken.enqueue(m.matchDataCache)

View File

@ -1,5 +1,6 @@
import copy import copy
import json import json
import threading
from common.log import logUtils as log from common.log import logUtils as log
from constants import dataTypes from constants import dataTypes
from constants import matchModModes from constants import matchModModes
@ -60,6 +61,7 @@ class match:
self.isTourney = isTourney self.isTourney = isTourney
self.isLocked = False # if True, users can't change slots/teams. Used in tourney matches self.isLocked = False # if True, users can't change slots/teams. Used in tourney matches
self.isStarting = False self.isStarting = False
self._lock = threading.Lock()
# Create all slots and reset them # Create all slots and reset them
self.slots = [] self.slots = []
@ -83,55 +85,55 @@ class match:
""" """
# General match info # General match info
# TODO: Test without safe copy, the error might have been caused by outdated python bytecode cache # 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 = [ struct = [
[safeMatch.matchID, dataTypes.UINT16], [self.matchID, dataTypes.UINT16],
[int(safeMatch.inProgress), dataTypes.BYTE], [int(self.inProgress), dataTypes.BYTE],
[0, dataTypes.BYTE], [0, dataTypes.BYTE],
[safeMatch.mods, dataTypes.UINT32], [self.mods, dataTypes.UINT32],
[safeMatch.matchName, dataTypes.STRING] [self.matchName, dataTypes.STRING]
] ]
if censored and safeMatch.matchPassword: if censored and self.matchPassword:
struct.append(["redacted", dataTypes.STRING]) struct.append(["redacted", dataTypes.STRING])
else: else:
struct.append([safeMatch.matchPassword, dataTypes.STRING]) struct.append([self.matchPassword, dataTypes.STRING])
struct.extend([ struct.extend([
[safeMatch.beatmapName, dataTypes.STRING], [self.beatmapName, dataTypes.STRING],
[safeMatch.beatmapID, dataTypes.UINT32], [self.beatmapID, dataTypes.UINT32],
[safeMatch.beatmapMD5, dataTypes.STRING] [self.beatmapMD5, dataTypes.STRING]
]) ])
# Slots status IDs, always 16 elements # Slots status IDs, always 16 elements
for i in range(0,16): 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 # Slot teams, always 16 elements
for i in range(0,16): 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 # Slot user ID. Write only if slot is occupied
for i in range(0,16): for i in range(0,16):
if safeMatch.slots[i].user is not None and safeMatch.slots[i].user in glob.tokens.tokens: if self.slots[i].user is not None and self.slots[i].user in glob.tokens.tokens:
struct.append([glob.tokens.tokens[safeMatch.slots[i].user].userID, dataTypes.UINT32]) struct.append([glob.tokens.tokens[self.slots[i].user].userID, dataTypes.UINT32])
# Other match data # Other match data
struct.extend([ struct.extend([
[safeMatch.hostUserID, dataTypes.SINT32], [self.hostUserID, dataTypes.SINT32],
[safeMatch.gameMode, dataTypes.BYTE], [self.gameMode, dataTypes.BYTE],
[safeMatch.matchScoringType, dataTypes.BYTE], [self.matchScoringType, dataTypes.BYTE],
[safeMatch.matchTeamType, dataTypes.BYTE], [self.matchTeamType, dataTypes.BYTE],
[safeMatch.matchModMode, dataTypes.BYTE], [self.matchModMode, dataTypes.BYTE],
]) ])
# Slot mods if free mod is enabled # 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): for i in range(0,16):
struct.append([safeMatch.slots[i].mods, dataTypes.UINT32]) struct.append([self.slots[i].mods, dataTypes.UINT32])
# Seed idk # Seed idk
# TODO: Implement this, it should be used for mania "random" mod # 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 return struct
@ -854,3 +856,11 @@ class match:
message = "The match is now empty." message = "The match is now empty."
chat.sendMessage("FokaBot", chanName, message) 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()