.BANCHO. Implemented packet streams for multiplayer

This commit is contained in:
Nyo 2016-10-04 23:43:02 +02:00
parent afbd8e7e8c
commit 795b6f09be
12 changed files with 187 additions and 287 deletions

View File

@ -111,7 +111,7 @@ def handle(userToken, packetData):
match.matchModMode = matchModModes.normal match.matchModMode = matchModModes.normal
# Send updated settings # Send updated settings
match.sendUpdate() 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

@ -2,7 +2,6 @@ from common.log import logUtils as log
from constants import clientPackets from constants import clientPackets
from constants import exceptions from constants import exceptions
from constants import serverPackets from constants import serverPackets
from events import joinMatchEvent
from objects import glob from objects import glob
@ -26,7 +25,7 @@ def handle(userToken, packetData):
match = glob.matches.matches[matchID] match = glob.matches.matches[matchID]
# Join that match # Join that match
joinMatchEvent.joinMatch(userToken, matchID, packetData["matchPassword"]) userToken.joinMatch(matchID)
# Give host to match creator # Give host to match creator
match.setHost(userID) match.setHost(userID)

View File

@ -3,38 +3,26 @@ from common.log import logUtils as log
from constants import clientPackets from constants import clientPackets
from constants import exceptions from constants import exceptions
from constants import serverPackets from constants import serverPackets
from helpers import chatHelper as chat
from objects import glob from objects import glob
def handle(userToken, packetData): def handle(userToken, packetData):
# read packet data # read packet data
packetData = clientPackets.joinMatch(packetData) packetData = clientPackets.joinMatch(packetData)
matchID = packetData["matchID"]
password = packetData["password"]
# Get match from ID # Get match from ID
joinMatch(userToken, packetData["matchID"], packetData["password"])
def joinMatch(userToken, matchID, password, isPasswordHashed = False):
try: try:
# Stop spectating
userToken.stopSpectating()
# Leave other matches
if userToken.matchID > -1 and userToken.matchID != matchID:
userToken.partMatch()
# Get usertoken data
userID = userToken.userID
# Make sure the match exists # Make sure the match exists
if matchID not in glob.matches.matches: if matchID not in glob.matches.matches:
raise exceptions.matchNotFoundException return
# Match exists, get object # Match exists, get object
match = glob.matches.matches[matchID] match = glob.matches.matches[matchID]
# Hash password if needed # Hash password if needed
if isPasswordHashed == False and password != "": if password != "":
password = generalUtils.stringMd5(password) password = generalUtils.stringMd5(password)
# Check password # Check password
@ -44,24 +32,7 @@ def joinMatch(userToken, matchID, password, isPasswordHashed = False):
raise exceptions.matchWrongPasswordException raise exceptions.matchWrongPasswordException
# Password is correct, join match # Password is correct, join match
result = match.userJoin(userID)
# Check if we've joined the match successfully
if not result:
raise exceptions.matchJoinErrorException
# Match joined, set matchID for usertoken
userToken.joinMatch(matchID) userToken.joinMatch(matchID)
# Send packets
userToken.enqueue(serverPackets.matchJoinSuccess(matchID))
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID))
except exceptions.matchNotFoundException:
userToken.enqueue(serverPackets.matchJoinFail())
log.warning("{} has tried to join a mp room, but it doesn't exist".format(userToken.username))
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))
except exceptions.matchJoinErrorException:
userToken.enqueue(serverPackets.matchJoinFail())
log.warning("{} has tried to join a mp room, but an error has occured".format(userToken.username))

View File

@ -21,7 +21,7 @@ def handle(userToken, _=None):
userToken.stopSpectating() userToken.stopSpectating()
# Part matches # Part matches
userToken.partMatch() userToken.leaveMatch()
# Part all joined channels # Part all joined channels
for i in userToken.joinedChannels: for i in userToken.joinedChannels:

View File

@ -24,8 +24,4 @@ def handle(userToken, packetData):
slotID = match.getUserSlotID(userID) slotID = match.getUserSlotID(userID)
# Enqueue frames to who's playing # Enqueue frames to who's playing
for i in range(0,16): glob.streams.broadcast(match.playingStreamName, serverPackets.matchFrames(slotID, packetData))
if match.slots[i].userID > -1 and match.slots[i].status == slotStatuses.playing:
token = glob.tokens.getTokenFromUserID(match.slots[i].userID)
if token is not None:
token.enqueue(serverPackets.matchFrames(slotID, packetData))

View File

@ -22,27 +22,4 @@ def handle(userToken, _):
if userToken.userID != match.hostUserID: if userToken.userID != match.hostUserID:
return return
# Make sure we have enough players match.start()
if match.countUsers() < 2 or match.checkTeams() == False:
return
# Change inProgress value
match.inProgress = True
# Set playing to ready players and set load, skip and complete to False
for i in range(0,16):
if (match.slots[i].status & slotStatuses.ready) > 0:
match.slots[i].status = slotStatuses.playing
match.slots[i].loaded = False
match.slots[i].skip = False
match.slots[i].complete = False
# Send match start packet
for i in range(0,16):
if (match.slots[i].status & slotStatuses.playing) > 0 and match.slots[i].userID != -1:
token = glob.tokens.getTokenFromUserID(match.slots[i].userID)
if token is not None:
token.enqueue(serverPackets.matchStart(matchID))
# Send updates
match.sendUpdate()

View File

@ -1,2 +1,2 @@
def handle(userToken, _=None): def handle(userToken, _=None):
userToken.partMatch() userToken.leaveMatch()

View File

@ -1,5 +1,5 @@
# TODO: Enqueue all
import copy import copy
import dill
from common import generalUtils from common import generalUtils
from common.constants import gameModes from common.constants import gameModes
@ -20,6 +20,7 @@ class slot:
self.status = slotStatuses.free self.status = slotStatuses.free
self.team = 0 self.team = 0
self.userID = -1 self.userID = -1
self.user = None
self.mods = 0 self.mods = 0
self.loaded = False self.loaded = False
self.skip = False self.skip = False
@ -27,22 +28,6 @@ class slot:
class match: class match:
"""Multiplayer match object""" """Multiplayer match object"""
matchID = 0
inProgress = False
mods = 0
matchName = ""
matchPassword = ""
beatmapName = ""
beatmapID = 0
beatmapMD5 = ""
slots = []
hostUserID = 0
gameMode = gameModes.STD
matchScoringType = matchScoringTypes.score
matchTeamType = matchTeamTypes.headToHead
matchModMode = matchModModes.normal
seed = 0
def __init__(self, matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID): def __init__(self, matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID):
""" """
Create a new match object Create a new match object
@ -57,6 +42,8 @@ class match:
hostUserID -- user id of the host hostUserID -- user id of the host
""" """
self.matchID = matchID self.matchID = matchID
self.streamName = "multi/{}".format(self.matchID)
self.playingStreamName = "{}/playing".format(self.streamName)
self.inProgress = False self.inProgress = False
self.mods = 0 self.mods = 0
self.matchName = matchName self.matchName = matchName
@ -69,7 +56,7 @@ class match:
self.beatmapMD5 = beatmapMD5 self.beatmapMD5 = beatmapMD5
self.hostUserID = hostUserID self.hostUserID = hostUserID
self.gameMode = gameMode self.gameMode = gameMode
self.matchScoringTypes = matchScoringTypes.score # default values self.matchScoringType = matchScoringTypes.score # default values
self.matchTeamType = matchTeamTypes.headToHead # default value self.matchTeamType = matchTeamTypes.headToHead # default value
self.matchModMode = matchModModes.normal # default value self.matchModMode = matchModModes.normal # default value
self.seed = 0 self.seed = 0
@ -79,6 +66,10 @@ class match:
for _ in range(0,16): for _ in range(0,16):
self.slots.append(slot()) self.slots.append(slot())
# Create streams
glob.streams.add(self.streamName)
glob.streams.add(self.playingStreamName)
# Create #multiplayer channel # Create #multiplayer channel
glob.channels.addTempChannel("#multi_{}".format(self.matchID)) glob.channels.addTempChannel("#multi_{}".format(self.matchID))
@ -109,9 +100,8 @@ class match:
# 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):
uid = self.slots[i].userID if self.slots[i].user is not None:
if uid > -1: struct.append([self.slots[i].user.userID, dataTypes.UINT32])
struct.append([uid, dataTypes.UINT32])
# Other match data # Other match data
struct.extend([ struct.extend([
@ -138,50 +128,37 @@ class match:
newHost -- new host userID newHost -- new host userID
""" """
slotID = self.getUserSlotID(newHost)
if slotID is None:
return
token = self.slots[slotID].user
self.hostUserID = newHost self.hostUserID = newHost
token.enqueue(serverPackets.matchTransferHost())
self.sendUpdates()
log.info("MPROOM{}: {} is now the host".format(self.matchID, token.username))
# Send host packet to new host def setSlot(self, slotID, status = None, team = None, user = -1, mods = None, loaded = None, skip = None, complete = None):
token = glob.tokens.getTokenFromUserID(newHost) #self.setSlot(i, slotStatuses.notReady, 0, user, 0)
if token is not None: if status is not None:
token.enqueue(serverPackets.matchTransferHost()) self.slots[slotID].status = status
log.info("MPROOM{}: {} is now the host".format(self.matchID, newHost)) if team is not None:
self.slots[slotID].team = team
def setSlot(self, slotID, slotStatus = None, slotTeam = None, slotUserID = None, slotMods = None, slotLoaded = None, slotSkip = None, slotComplete = None): if user is not -1:
""" self.slots[slotID].user = user
Set a slot to a specific userID and status
slotID -- id of that slot (0-15) if mods is not -1:
slotStatus -- see slotStatuses.py self.slots[slotID].mods = mods
slotTeam -- team id
slotUserID -- user ID of user in that slot
slotMods -- mods enabled in that slot. 0 if not free mod.
slotLoaded -- loaded status True/False
slotSkip -- skip status True/False
slotComplete -- completed status True/False
If Null is passed, that value won't be edited if loaded is not None:
""" self.slots[slotID].loaded = loaded
if slotStatus is not None:
self.slots[slotID].status = slotStatus
if slotTeam is not None: if skip is not None:
self.slots[slotID].team = slotTeam self.slots[slotID].skip = skip
if slotUserID is not None: if complete is not None:
self.slots[slotID].userID = slotUserID self.slots[slotID].complete = complete
if slotMods is not None:
self.slots[slotID].mods = slotMods
if slotLoaded is not None:
self.slots[slotID].loaded = slotLoaded
if slotSkip is not None:
self.slots[slotID].skip = slotSkip
if slotComplete is not None:
self.slots[slotID].complete = slotComplete
def setSlotMods(self, slotID, mods): def setSlotMods(self, slotID, mods):
""" """
@ -191,8 +168,8 @@ class match:
mods -- new mods mods -- new mods
""" """
# Set new slot data and send update # Set new slot data and send update
self.setSlot(slotID, None, None, None, mods) self.setSlot(slotID, mods=mods)
self.sendUpdate() self.sendUpdates()
log.info("MPROOM{}: Slot{} mods changed to {}".format(self.matchID, slotID, mods)) log.info("MPROOM{}: Slot{} mods changed to {}".format(self.matchID, slotID, mods))
def toggleSlotReady(self, slotID): def toggleSlotReady(self, slotID):
@ -208,8 +185,8 @@ class match:
newStatus = slotStatuses.notReady newStatus = slotStatuses.notReady
else: else:
newStatus = slotStatuses.ready newStatus = slotStatuses.ready
self.setSlot(slotID, newStatus, None, None, None) self.setSlot(slotID, newStatus)
self.sendUpdate() self.sendUpdates()
log.info("MPROOM{}: Slot{} changed ready status to {}".format(self.matchID, slotID, self.slots[slotID].status)) log.info("MPROOM{}: Slot{} changed ready status to {}".format(self.matchID, slotID, self.slots[slotID].status))
def toggleSlotLock(self, slotID): def toggleSlotLock(self, slotID):
@ -219,26 +196,21 @@ class match:
slotID -- slot number slotID -- slot number
""" """
# Get token of user in that slot (if there's someone)
if self.slots[slotID].userID > -1:
token = glob.tokens.getTokenFromUserID(self.slots[slotID].userID)
else:
token = None
# Check if slot is already locked # Check if slot is already locked
if self.slots[slotID].status == slotStatuses.locked: if self.slots[slotID].status == slotStatuses.locked:
newStatus = slotStatuses.free newStatus = slotStatuses.free
else: else:
newStatus = slotStatuses.locked newStatus = slotStatuses.locked
# Send updated settings to kicked user, so he returns to lobby
if self.slots[slotID].user is not None:
self.slots[slotID].user.enqueue(serverPackets.updateMatch(self.matchID))
# Set new slot status # Set new slot status
self.setSlot(slotID, newStatus, 0, -1, 0) self.setSlot(slotID, newStatus, 0, -1, 0)
if token is not None:
# Send updated settings to kicked user, so he returns to lobby
token.enqueue(serverPackets.updateMatch(self.matchID))
# Send updates to everyone else # Send updates to everyone else
self.sendUpdate() self.sendUpdates()
log.info("MPROOM{}: Slot{} {}".format(self.matchID, slotID, "locked" if newStatus == slotStatuses.locked else "unlocked")) log.info("MPROOM{}: Slot{} {}".format(self.matchID, slotID, "locked" if newStatus == slotStatuses.locked else "unlocked"))
def playerLoaded(self, userID): def playerLoaded(self, userID):
@ -269,12 +241,7 @@ class match:
def allPlayersLoaded(self): def allPlayersLoaded(self):
"""Send allPlayersLoaded packet to every playing usr in match""" """Send allPlayersLoaded packet to every playing usr in match"""
for i in range(0,16): glob.streams.broadcast(self.playingStreamName, serverPackets.allPlayersLoaded())
if self.slots[i].userID > -1 and self.slots[i].status == slotStatuses.playing:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID)
if token is not None:
token.enqueue(serverPackets.allPlayersLoaded())
log.info("MPROOM{}: All players loaded! Match starting...".format(self.matchID)) log.info("MPROOM{}: All players loaded! Match starting...".format(self.matchID))
def playerSkip(self, userID): def playerSkip(self, userID):
@ -292,12 +259,7 @@ class match:
log.info("MPROOM{}: User {} skipped".format(self.matchID, userID)) log.info("MPROOM{}: User {} skipped".format(self.matchID, userID))
# Send skip packet to every playing user # Send skip packet to every playing user
for i in range(0,16): glob.streams.broadcast(self.playingStreamName, serverPackets.playerSkipped(self.slots[slotID].user.userID))
uid = self.slots[i].userID
if (self.slots[i].status & slotStatuses.playing > 0) and uid > -1:
token = glob.tokens.getTokenFromUserID(uid)
if token is not None:
token.enqueue(serverPackets.playerSkipped(uid))
# Check all skipped # Check all skipped
total = 0 total = 0
@ -313,12 +275,7 @@ class match:
def allPlayersSkipped(self): def allPlayersSkipped(self):
"""Send allPlayersSkipped packet to every playing usr in match""" """Send allPlayersSkipped packet to every playing usr in match"""
for i in range(0,16): glob.streams.broadcast(self.playingStreamName, serverPackets.allPlayersSkipped())
if self.slots[i].userID > -1 and self.slots[i].status == slotStatuses.playing:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID)
if token is not None:
token.enqueue(serverPackets.allPlayersSkipped())
log.info("MPROOM{}: All players have skipped!".format(self.matchID)) log.info("MPROOM{}: All players have skipped!".format(self.matchID))
def playerCompleted(self, userID): def playerCompleted(self, userID):
@ -330,7 +287,7 @@ class match:
slotID = self.getUserSlotID(userID) slotID = self.getUserSlotID(userID)
if slotID is None: if slotID is None:
return return
self.setSlot(slotID, None, None, None, None, None, None, True) self.setSlot(slotID, complete=True)
# Console output # Console output
log.info("MPROOM{}: User {} has completed his play".format(self.matchID, userID)) log.info("MPROOM{}: User {} has completed his play".format(self.matchID, userID))
@ -355,21 +312,20 @@ class match:
# Reset slots # Reset slots
for i in range(0,16): for i in range(0,16):
if self.slots[i].userID > -1 and self.slots[i].status == slotStatuses.playing: if self.slots[i].user is not None and self.slots[i].status == slotStatuses.playing:
self.slots[i].status = slotStatuses.notReady self.slots[i].status = slotStatuses.notReady
self.slots[i].loaded = False self.slots[i].loaded = False
self.slots[i].skip = False self.slots[i].skip = False
self.slots[i].complete = False self.slots[i].complete = False
# Send match update # Send match update
self.sendUpdate() self.sendUpdates()
# Send match complete # Send match complete
for i in range(0,16): glob.streams.broadcast(self.streamName, serverPackets.matchComplete())
if self.slots[i].userID > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID) # Destroy playing stream
if token is not None: glob.streams.remove(self.playingStreamName)
token.enqueue(serverPackets.matchComplete())
# Console output # Console output
log.info("MPROOM{}: Match completed".format(self.matchID)) log.info("MPROOM{}: Match completed".format(self.matchID))
@ -381,11 +337,11 @@ class match:
return -- slot id if found, None if user is not in room return -- slot id if found, None if user is not in room
""" """
for i in range(0,16): for i in range(0,16):
if self.slots[i].userID == userID: if self.slots[i].user is not None and self.slots[i].user.userID == userID:
return i return i
return None return None
def userJoin(self, userID): def userJoin(self, user):
""" """
Add someone to users in match Add someone to users in match
@ -395,39 +351,38 @@ class match:
# Make sure we're not in this match # Make sure we're not in this match
for i in range(0,16): for i in range(0,16):
if self.slots[i].userID == userID: if self.slots[i].user == user:
# Set bugged slot to free # Set bugged slot to free
self.setSlot(i, slotStatuses.free, 0, -1, 0) self.setSlot(i, slotStatuses.free, 0, None, 0)
# Find first free slot # Find first free slot
for i in range(0,16): for i in range(0,16):
if self.slots[i].status == slotStatuses.free: if self.slots[i].status == slotStatuses.free:
# Occupy slot # Occupy slot
self.setSlot(i, slotStatuses.notReady, 0, userID, 0) self.setSlot(i, slotStatuses.notReady, 0, user, 0)
# Send updated match data # Send updated match data
self.sendUpdate() self.sendUpdates()
# Console output # Console output
log.info("MPROOM{}: {} joined the room".format(self.matchID, userID)) log.info("MPROOM{}: {} joined the room".format(self.matchID, user.username))
return True return True
return False return False
def userLeft(self, userID): def userLeft(self, user):
""" """
Remove someone from users in match Remove someone from users in match
userID -- user if of the user userID -- user if of the user
""" """
# Make sure the user is in room # Make sure the user is in room
slotID = self.getUserSlotID(userID) slotID = self.getUserSlotID(user.userID)
if slotID is None: if slotID is None:
return return
# Set that slot to free # Set that slot to free
self.setSlot(slotID, slotStatuses.free, 0, -1, 0) self.setSlot(slotID, slotStatuses.free, 0, None, 0)
# Check if everyone left # Check if everyone left
if self.countUsers() == 0: if self.countUsers() == 0:
@ -437,19 +392,18 @@ class match:
return return
# Check if host left # Check if host left
if userID == self.hostUserID: if user.userID == self.hostUserID:
# Give host to someone else # Give host to someone else
for i in range(0,16): for i in range(0,16):
uid = self.slots[i].userID if self.slots[i].user is not None:
if uid > -1: self.setHost(self.slots[i].user.userID)
self.setHost(uid)
break break
# Send updated match data # Send updated match data
self.sendUpdate() self.sendUpdates()
# Console output # Console output
log.info("MPROOM{}: {} left the room".format(self.matchID, userID)) log.info("MPROOM{}: {} left the room".format(self.matchID, user.username))
def userChangeSlot(self, userID, newSlotID): def userChangeSlot(self, userID, newSlotID):
""" """
@ -464,20 +418,21 @@ class match:
return return
# Make sure there is no one inside new slot # Make sure there is no one inside new slot
if self.slots[newSlotID].userID > -1 and self.slots[newSlotID].status != slotStatuses.free: if self.slots[newSlotID].user is not None and self.slots[newSlotID].status != slotStatuses.free:
return return
# Get old slot data # Get old slot data
oldData = copy.deepcopy(self.slots[oldSlotID]) oldData = dill.copy(self.slots[oldSlotID])
#oldData = copy.deepcopy(self.slots[oldSlotID])
# Free old slot # Free old slot
self.setSlot(oldSlotID, slotStatuses.free, 0, -1, 0) self.setSlot(oldSlotID, slotStatuses.free, 0, None, 0)
# Occupy new slot # Occupy new slot
self.setSlot(newSlotID, oldData.status, oldData.team, oldData.userID, oldData.mods) self.setSlot(newSlotID, oldData.status, oldData.team, oldData.user, oldData.mods)
# Send updated match data # Send updated match data
self.sendUpdate() self.sendUpdates()
# Console output # Console output
log.info("MPROOM{}: {} moved to slot {}".format(self.matchID, userID, newSlotID)) log.info("MPROOM{}: {} moved to slot {}".format(self.matchID, userID, newSlotID))
@ -494,14 +449,10 @@ class match:
self.matchPassword = "" self.matchPassword = ""
# Send password change to every user in match # Send password change to every user in match
for i in range(0,16): glob.streams.broadcast(self.streamName, serverPackets.changeMatchPassword(self.matchPassword))
if self.slots[i].userID > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID)
if token is not None:
token.enqueue(serverPackets.changeMatchPassword(self.matchPassword))
# Send new match settings too # Send new match settings too
self.sendUpdate() self.sendUpdates()
# Console output # Console output
log.info("MPROOM{}: Password changed to {}".format(self.matchID, self.matchPassword)) log.info("MPROOM{}: Password changed to {}".format(self.matchID, self.matchPassword))
@ -514,7 +465,7 @@ class match:
""" """
# Set new mods and send update # Set new mods and send update
self.mods = mods self.mods = mods
self.sendUpdate() self.sendUpdates()
log.info("MPROOM{}: Mods changed to {}".format(self.matchID, self.mods)) log.info("MPROOM{}: Mods changed to {}".format(self.matchID, self.mods))
def userHasBeatmap(self, userID, has = True): def userHasBeatmap(self, userID, has = True):
@ -533,7 +484,7 @@ class match:
self.setSlot(slotID, slotStatuses.noMap if not has else slotStatuses.notReady) self.setSlot(slotID, slotStatuses.noMap if not has else slotStatuses.notReady)
# Send updates # Send updates
self.sendUpdate() self.sendUpdates()
def transferHost(self, slotID): def transferHost(self, slotID):
""" """
@ -542,15 +493,14 @@ class match:
slotID -- ID of slot slotID -- ID of slot
""" """
# Make sure there is someone in that slot # Make sure there is someone in that slot
uid = self.slots[slotID].userID if self.slots[slotID].user is None:
if uid == -1:
return return
# Transfer host # Transfer host
self.setHost(uid) self.setHost(self.slots[slotID].user.userID)
# Send updates # Send updates
self.sendUpdate() self.sendUpdates()
def playerFailed(self, userID): def playerFailed(self, userID):
""" """
@ -564,12 +514,7 @@ class match:
return return
# Send packet to everyone # Send packet to everyone
for i in range(0,16): glob.streams.broadcast(self.playingStreamName, serverPackets.playerFailed(slotID))
uid = self.slots[i].userID
if uid > -1:
token = glob.tokens.getTokenFromUserID(uid)
if token is not None:
token.enqueue(serverPackets.playerFailed(slotID))
# Console output # Console output
log.info("MPROOM{}: {} has failed!".format(self.matchID, userID)) log.info("MPROOM{}: {} has failed!".format(self.matchID, userID))
@ -604,7 +549,7 @@ class match:
""" """
c = 0 c = 0
for i in range(0,16): for i in range(0,16):
if self.slots[i].userID > -1: if self.slots[i].user is not None:
c+=1 c+=1
return c return c
@ -622,17 +567,10 @@ class match:
# Update slot and send update # Update slot and send update
newTeam = matchTeams.blue if self.slots[slotID].team == matchTeams.red else matchTeams.red newTeam = matchTeams.blue if self.slots[slotID].team == matchTeams.red else matchTeams.red
self.setSlot(slotID, None, newTeam) self.setSlot(slotID, None, newTeam)
self.sendUpdate() self.sendUpdates()
def sendUpdate(self): def sendUpdates(self):
# Send to users in room glob.streams.broadcast(self.streamName, serverPackets.updateMatch(self.matchID))
for i in range(0,16):
if self.slots[i].userID > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID)
if token is not None:
token.enqueue(serverPackets.updateMatch(self.matchID))
# Send to users in lobby
glob.streams.broadcast("lobby", serverPackets.updateMatch(self.matchID)) glob.streams.broadcast("lobby", serverPackets.updateMatch(self.matchID))
def checkTeams(self): def checkTeams(self):
@ -641,14 +579,14 @@ class match:
return -- True if valid, False if invalid return -- True if valid, False if invalid
""" """
if match.matchTeamType != matchTeamTypes.teamVs or matchTeamTypes != matchTeamTypes.tagTeamVs: if self.matchTeamType != matchTeamTypes.teamVs or self.matchTeamType != matchTeamTypes.tagTeamVs:
# Teams are always valid if we have no teams # Teams are always valid if we have no teams
return True return True
# We have teams, check if they are valid # We have teams, check if they are valid
firstTeam = -1 firstTeam = -1
for i in range(0,16): for i in range(0,16):
if self.slots[i].userID > -1 and (self.slots[i].status&slotStatuses.noMap) == 0: if self.slots[i].user is not None and (self.slots[i].status&slotStatuses.noMap) == 0:
if firstTeam == -1: if firstTeam == -1:
firstTeam = self.slots[i].team firstTeam = self.slots[i].team
elif firstTeam != self.slots[i].team: elif firstTeam != self.slots[i].team:
@ -657,3 +595,30 @@ class match:
log.warning("MPROOM{}: Invalid teams!".format(self.matchID)) log.warning("MPROOM{}: Invalid teams!".format(self.matchID))
return False return False
def start(self):
# Make sure we have enough players
if self.countUsers() < 2 or not self.checkTeams():
return
# Create playing channel
glob.streams.add(self.playingStreamName)
# Change inProgress value
match.inProgress = True
# Set playing to ready players and set load, skip and complete to False
# Make clients join playing stream
for i in range(0, 16):
if (self.slots[i].status & slotStatuses.ready) > 0:
self.slots[i].status = slotStatuses.playing
self.slots[i].loaded = False
self.slots[i].skip = False
self.slots[i].complete = False
self.slots[i].user.joinStream(self.playingStreamName)
# Send match start packet
glob.streams.broadcast(self.playingStreamName, serverPackets.matchStart(self.matchID))
# Send updates
self.sendUpdates()

View File

@ -21,34 +21,12 @@ class matchList:
hostUserID -- user id of who created the match hostUserID -- user id of who created the match
return -- match ID return -- match ID
""" """
# Add a new match to matches list # Add a new match to matches list and create its stream
matchID = self.lastID matchID = self.lastID
self.lastID+=1 self.lastID+=1
self.matches[matchID] = match.match(matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID) self.matches[matchID] = match.match(matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID)
return matchID return matchID
'''def lobbyUserJoin(self, userID):
"""
Add userID to users in lobby
userID -- user who joined mp lobby
"""
# Make sure the user is not already in mp lobby
if userID not in self.usersInLobby:
# We don't need to join #lobby, client will automatically send a packet for it
self.usersInLobby.append(userID)
def lobbyUserPart(self, userID):
"""
Remove userID from users in lobby
userID -- user who left mp lobby
"""
# Make sure the user is in mp lobby
if userID in self.usersInLobby:
# Part lobby and #lobby channel
self.usersInLobby.remove(userID)'''
def disposeMatch(self, matchID): def disposeMatch(self, matchID):
""" """
Destroy match object with id = matchID Destroy match object with id = matchID
@ -59,8 +37,10 @@ class matchList:
if matchID not in self.matches: if matchID not in self.matches:
return return
# Remove match object # Remove match object and stream
self.matches.pop(matchID) match = self.matches.pop(matchID)
glob.streams.remove(match.streamName)
glob.streams.remove(match.playingStreamName)
# Send match dispose packet to everyone in lobby # Send match dispose packet to everyone in lobby
glob.streams.broadcast("lobby", serverPackets.disposeMatch(matchID)) glob.streams.broadcast("lobby", serverPackets.disposeMatch(matchID))

View File

@ -216,47 +216,6 @@ class token:
# Set our spectating user to 0 # Set our spectating user to 0
self.spectating = None self.spectating = None
def partMatch(self):
# Make sure we are in a match
if self.matchID == -1:
return
# Part #multiplayer channel
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True)
# Make sure the match exists
if self.matchID not in glob.matches.matches:
return
# The match exists, get object
match = glob.matches.matches[self.matchID]
# Set slot to free
match.userLeft(self.userID)
# Set usertoken match to -1
self.matchID = -1
'''def addSpectator(self, user):
"""
Add userID to our spectators
user -- user object
"""
# Add userID to spectators if not already in
if user not in self.spectators:
self.spectators.append(user)
def removeSpectator(self, userID):
"""
Remove userID from our spectators
userID -- old spectator userID
"""
# Remove spectator
if userID in self.spectators:
self.spectators.remove(userID)'''
def setCountry(self, countryID): def setCountry(self, countryID):
""" """
Set country to countryID Set country to countryID
@ -281,13 +240,66 @@ class token:
"""Set a new away message""" """Set a new away message"""
self.awayMessage = __awayMessage self.awayMessage = __awayMessage
def joinMatch(self, matchID): def joinMatch(self, matchID):
""" """
Set match to matchID Set match to matchID, join match stream and channel
matchID -- new match ID matchID -- new match ID
""" """
# Make sure the match exists
if matchID not in glob.matches.matches:
return
# Match exists, get object
match = glob.matches.matches[matchID]
# Stop spectating
self.stopSpectating()
# Leave other matches
if self.matchID > -1 and self.matchID != matchID:
self.leaveMatch()
# Try to join match
joined = match.userJoin(self)
if not joined:
self.enqueue(serverPackets.matchJoinFail())
return
# Set matchID, join stream, channel and send packet
self.matchID = matchID self.matchID = matchID
self.joinStream(match.streamName)
chat.joinChannel(token=self, channel="#multi_{}".format(self.matchID))
self.enqueue(serverPackets.matchJoinSuccess(matchID))
def leaveMatch(self):
"""
Leave joined match, match stream and match channel
:return:
"""
# Make sure we are in a match
if self.matchID == -1:
return
# Part #multiplayer channel and streams (/ and /playing)
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True)
self.leaveStream("multi/{}".format(self.matchID))
self.leaveStream("multi/{}/playing".format(self.matchID)) # optional
# Make sure the match exists
if self.matchID not in glob.matches.matches:
return
# The match exists, get object
match = glob.matches.matches[self.matchID]
# Set slot to free
match.userLeft(self)
# Set usertoken match to -1
self.matchID = -1
def kick(self, message="You have been kicked from the server. Please login again."): def kick(self, message="You have been kicked from the server. Please login again."):
""" """

View File

@ -40,4 +40,5 @@ class stream:
:return: :return:
""" """
for i in self.clients: for i in self.clients:
i.enqueue(data) if i is not None:
i.enqueue(data)

View File

@ -13,7 +13,6 @@ class streamList:
""" """
if name not in self.streams: if name not in self.streams:
self.streams[name] = stream.stream(name) self.streams[name] = stream.stream(name)
print(str(self.streams))
def remove(self, name): def remove(self, name):
""" """