2016-10-02 20:48:14 +00:00
|
|
|
import copy
|
2016-10-04 21:43:02 +00:00
|
|
|
import dill
|
2016-10-02 20:48:14 +00:00
|
|
|
|
|
|
|
from common import generalUtils
|
|
|
|
from common.constants import gameModes
|
|
|
|
from common.log import logUtils as log
|
|
|
|
from constants import dataTypes
|
|
|
|
from constants import matchModModes
|
2016-05-18 17:12:46 +00:00
|
|
|
from constants import matchScoringTypes
|
|
|
|
from constants import matchTeamTypes
|
|
|
|
from constants import matchTeams
|
2016-10-02 20:48:14 +00:00
|
|
|
from constants import serverPackets
|
|
|
|
from constants import slotStatuses
|
2016-07-14 10:37:07 +00:00
|
|
|
from helpers import chatHelper as chat
|
2016-10-02 20:48:14 +00:00
|
|
|
from objects import glob
|
|
|
|
|
2016-09-02 10:41:19 +00:00
|
|
|
|
2016-09-02 15:45:10 +00:00
|
|
|
class slot:
|
2016-09-02 10:41:19 +00:00
|
|
|
def __init__(self):
|
|
|
|
self.status = slotStatuses.free
|
|
|
|
self.team = 0
|
|
|
|
self.userID = -1
|
2016-10-04 21:43:02 +00:00
|
|
|
self.user = None
|
2016-09-02 10:41:19 +00:00
|
|
|
self.mods = 0
|
|
|
|
self.loaded = False
|
|
|
|
self.skip = False
|
|
|
|
self.complete = False
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-09-02 15:45:10 +00:00
|
|
|
class match:
|
2016-04-19 17:40:59 +00:00
|
|
|
"""Multiplayer match object"""
|
2016-08-17 14:41:05 +00:00
|
|
|
def __init__(self, matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID):
|
2016-04-19 17:40:59 +00:00
|
|
|
"""
|
|
|
|
Create a new match object
|
|
|
|
|
2016-08-17 14:41:05 +00:00
|
|
|
matchID -- match progressive identifier
|
|
|
|
matchName -- match name, string
|
|
|
|
matchPassword -- match md5 password. Leave empty for no password
|
|
|
|
beatmapID -- beatmap ID
|
|
|
|
beatmapName -- beatmap name, string
|
|
|
|
beatmapMD5 -- beatmap md5 hash, string
|
|
|
|
gameMode -- game mode ID. See gameModes.py
|
|
|
|
hostUserID -- user id of the host
|
2016-04-19 17:40:59 +00:00
|
|
|
"""
|
2016-08-17 14:41:05 +00:00
|
|
|
self.matchID = matchID
|
2016-10-04 21:43:02 +00:00
|
|
|
self.streamName = "multi/{}".format(self.matchID)
|
|
|
|
self.playingStreamName = "{}/playing".format(self.streamName)
|
2016-04-19 17:40:59 +00:00
|
|
|
self.inProgress = False
|
|
|
|
self.mods = 0
|
2016-08-17 14:41:05 +00:00
|
|
|
self.matchName = matchName
|
2016-09-02 10:41:19 +00:00
|
|
|
if matchPassword != "":
|
2016-10-02 20:48:14 +00:00
|
|
|
self.matchPassword = generalUtils.stringMd5(matchPassword)
|
2016-09-02 10:41:19 +00:00
|
|
|
else:
|
|
|
|
self.matchPassword = ""
|
2016-08-17 14:41:05 +00:00
|
|
|
self.beatmapID = beatmapID
|
|
|
|
self.beatmapName = beatmapName
|
|
|
|
self.beatmapMD5 = beatmapMD5
|
|
|
|
self.hostUserID = hostUserID
|
|
|
|
self.gameMode = gameMode
|
2016-10-04 21:43:02 +00:00
|
|
|
self.matchScoringType = matchScoringTypes.score # default values
|
2016-04-19 17:40:59 +00:00
|
|
|
self.matchTeamType = matchTeamTypes.headToHead # default value
|
|
|
|
self.matchModMode = matchModModes.normal # default value
|
|
|
|
self.seed = 0
|
|
|
|
|
|
|
|
# Create all slots and reset them
|
|
|
|
self.slots = []
|
|
|
|
for _ in range(0,16):
|
2016-09-02 10:41:19 +00:00
|
|
|
self.slots.append(slot())
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
# Create streams
|
|
|
|
glob.streams.add(self.streamName)
|
|
|
|
glob.streams.add(self.playingStreamName)
|
|
|
|
|
2016-07-14 10:37:07 +00:00
|
|
|
# Create #multiplayer channel
|
|
|
|
glob.channels.addTempChannel("#multi_{}".format(self.matchID))
|
|
|
|
|
2016-04-19 17:40:59 +00:00
|
|
|
def getMatchData(self):
|
|
|
|
"""
|
|
|
|
Return binary match data structure for packetHelper
|
|
|
|
"""
|
|
|
|
# General match info
|
|
|
|
struct = [
|
2016-09-02 15:16:22 +00:00
|
|
|
[self.matchID, dataTypes.UINT16],
|
|
|
|
[int(self.inProgress), dataTypes.BYTE],
|
|
|
|
[0, dataTypes.BYTE],
|
|
|
|
[self.mods, dataTypes.UINT32],
|
|
|
|
[self.matchName, dataTypes.STRING],
|
|
|
|
[self.matchPassword, dataTypes.STRING],
|
|
|
|
[self.beatmapName, dataTypes.STRING],
|
|
|
|
[self.beatmapID, dataTypes.UINT32],
|
|
|
|
[self.beatmapMD5, dataTypes.STRING],
|
2016-04-19 17:40:59 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
# Slots status IDs, always 16 elements
|
|
|
|
for i in range(0,16):
|
2016-09-02 15:16:22 +00:00
|
|
|
struct.append([self.slots[i].status, dataTypes.BYTE])
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Slot teams, always 16 elements
|
|
|
|
for i in range(0,16):
|
2016-09-02 15:16:22 +00:00
|
|
|
struct.append([self.slots[i].team, dataTypes.BYTE])
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Slot user ID. Write only if slot is occupied
|
|
|
|
for i in range(0,16):
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[i].user is not None:
|
|
|
|
struct.append([self.slots[i].user.userID, dataTypes.UINT32])
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Other match data
|
|
|
|
struct.extend([
|
2016-09-02 15:16:22 +00:00
|
|
|
[self.hostUserID, dataTypes.SINT32],
|
|
|
|
[self.gameMode, dataTypes.BYTE],
|
|
|
|
[self.matchScoringType, dataTypes.BYTE],
|
|
|
|
[self.matchTeamType, dataTypes.BYTE],
|
|
|
|
[self.matchModMode, dataTypes.BYTE],
|
2016-04-19 17:40:59 +00:00
|
|
|
])
|
|
|
|
|
|
|
|
# Slot mods if free mod is enabled
|
|
|
|
if self.matchModMode == matchModModes.freeMod:
|
|
|
|
for i in range(0,16):
|
2016-09-02 15:16:22 +00:00
|
|
|
struct.append([self.slots[i].mods, dataTypes.UINT32])
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Seed idk
|
2016-09-02 15:16:22 +00:00
|
|
|
struct.append([self.seed, dataTypes.UINT32])
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
return struct
|
|
|
|
|
|
|
|
def setHost(self, newHost):
|
|
|
|
"""
|
|
|
|
Set room host to newHost and send him host packet
|
|
|
|
|
|
|
|
newHost -- new host userID
|
|
|
|
"""
|
2016-10-04 21:43:02 +00:00
|
|
|
slotID = self.getUserSlotID(newHost)
|
|
|
|
if slotID is None:
|
|
|
|
return
|
|
|
|
token = self.slots[slotID].user
|
2016-04-19 17:40:59 +00:00
|
|
|
self.hostUserID = newHost
|
2016-10-04 21:43:02 +00:00
|
|
|
token.enqueue(serverPackets.matchTransferHost())
|
|
|
|
self.sendUpdates()
|
|
|
|
log.info("MPROOM{}: {} is now the host".format(self.matchID, token.username))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
def setSlot(self, slotID, status = None, team = None, user = -1, mods = None, loaded = None, skip = None, complete = None):
|
|
|
|
#self.setSlot(i, slotStatuses.notReady, 0, user, 0)
|
|
|
|
if status is not None:
|
|
|
|
self.slots[slotID].status = status
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
if team is not None:
|
|
|
|
self.slots[slotID].team = team
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
if user is not -1:
|
|
|
|
self.slots[slotID].user = user
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
if mods is not -1:
|
|
|
|
self.slots[slotID].mods = mods
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
if loaded is not None:
|
|
|
|
self.slots[slotID].loaded = loaded
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
if skip is not None:
|
|
|
|
self.slots[slotID].skip = skip
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
if complete is not None:
|
|
|
|
self.slots[slotID].complete = complete
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def setSlotMods(self, slotID, mods):
|
|
|
|
"""
|
|
|
|
Set slotID mods. Same as calling setSlot and then sendUpdate
|
|
|
|
|
|
|
|
slotID -- slot number
|
|
|
|
mods -- new mods
|
|
|
|
"""
|
|
|
|
# Set new slot data and send update
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setSlot(slotID, mods=mods)
|
|
|
|
self.sendUpdates()
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: Slot{} mods changed to {}".format(self.matchID, slotID, mods))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def toggleSlotReady(self, slotID):
|
|
|
|
"""
|
|
|
|
Switch slotID ready/not ready status
|
|
|
|
Same as calling setSlot and then sendUpdate
|
|
|
|
|
|
|
|
slotID -- slot number
|
|
|
|
"""
|
|
|
|
# Update ready status and setnd update
|
2016-09-02 10:41:19 +00:00
|
|
|
oldStatus = self.slots[slotID].status
|
2016-04-19 17:40:59 +00:00
|
|
|
if oldStatus == slotStatuses.ready:
|
|
|
|
newStatus = slotStatuses.notReady
|
|
|
|
else:
|
|
|
|
newStatus = slotStatuses.ready
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setSlot(slotID, newStatus)
|
|
|
|
self.sendUpdates()
|
2016-09-02 10:41:19 +00:00
|
|
|
log.info("MPROOM{}: Slot{} changed ready status to {}".format(self.matchID, slotID, self.slots[slotID].status))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def toggleSlotLock(self, slotID):
|
|
|
|
"""
|
|
|
|
Lock a slot
|
|
|
|
Same as calling setSlot and then sendUpdate
|
|
|
|
|
|
|
|
slotID -- slot number
|
|
|
|
"""
|
|
|
|
# Check if slot is already locked
|
2016-09-02 10:41:19 +00:00
|
|
|
if self.slots[slotID].status == slotStatuses.locked:
|
2016-04-19 17:40:59 +00:00
|
|
|
newStatus = slotStatuses.free
|
|
|
|
else:
|
|
|
|
newStatus = slotStatuses.locked
|
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
# 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))
|
|
|
|
|
2016-04-19 17:40:59 +00:00
|
|
|
# Set new slot status
|
|
|
|
self.setSlot(slotID, newStatus, 0, -1, 0)
|
|
|
|
|
|
|
|
# Send updates to everyone else
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: Slot{} {}".format(self.matchID, slotID, "locked" if newStatus == slotStatuses.locked else "unlocked"))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def playerLoaded(self, userID):
|
|
|
|
"""
|
|
|
|
Set a player loaded status to True
|
|
|
|
|
|
|
|
userID -- ID of user
|
|
|
|
"""
|
|
|
|
slotID = self.getUserSlotID(userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if slotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Set loaded to True
|
2016-09-02 10:41:19 +00:00
|
|
|
self.slots[slotID].loaded = True
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: User {} loaded".format(self.matchID, userID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Check all loaded
|
|
|
|
total = 0
|
|
|
|
loaded = 0
|
|
|
|
for i in range(0,16):
|
2016-09-02 10:41:19 +00:00
|
|
|
if self.slots[i].status == slotStatuses.playing:
|
2016-04-19 17:40:59 +00:00
|
|
|
total+=1
|
2016-09-02 15:45:10 +00:00
|
|
|
if self.slots[i].loaded:
|
2016-04-19 17:40:59 +00:00
|
|
|
loaded+=1
|
|
|
|
|
|
|
|
if total == loaded:
|
|
|
|
self.allPlayersLoaded()
|
|
|
|
|
|
|
|
def allPlayersLoaded(self):
|
|
|
|
"""Send allPlayersLoaded packet to every playing usr in match"""
|
2016-10-04 21:43:02 +00:00
|
|
|
glob.streams.broadcast(self.playingStreamName, serverPackets.allPlayersLoaded())
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: All players loaded! Match starting...".format(self.matchID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def playerSkip(self, userID):
|
|
|
|
"""
|
|
|
|
Set a player skip status to True
|
|
|
|
|
|
|
|
userID -- ID of user
|
|
|
|
"""
|
|
|
|
slotID = self.getUserSlotID(userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if slotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Set skip to True
|
2016-09-02 10:41:19 +00:00
|
|
|
self.slots[slotID].skip = True
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: User {} skipped".format(self.matchID, userID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-09-02 10:41:19 +00:00
|
|
|
# Send skip packet to every playing user
|
2016-10-04 21:43:02 +00:00
|
|
|
glob.streams.broadcast(self.playingStreamName, serverPackets.playerSkipped(self.slots[slotID].user.userID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Check all skipped
|
|
|
|
total = 0
|
|
|
|
skipped = 0
|
|
|
|
for i in range(0,16):
|
2016-09-02 10:41:19 +00:00
|
|
|
if self.slots[i].status == slotStatuses.playing:
|
2016-04-19 17:40:59 +00:00
|
|
|
total+=1
|
2016-09-02 15:45:10 +00:00
|
|
|
if self.slots[i].skip:
|
2016-04-19 17:40:59 +00:00
|
|
|
skipped+=1
|
|
|
|
|
|
|
|
if total == skipped:
|
|
|
|
self.allPlayersSkipped()
|
|
|
|
|
|
|
|
def allPlayersSkipped(self):
|
|
|
|
"""Send allPlayersSkipped packet to every playing usr in match"""
|
2016-10-04 21:43:02 +00:00
|
|
|
glob.streams.broadcast(self.playingStreamName, serverPackets.allPlayersSkipped())
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: All players have skipped!".format(self.matchID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def playerCompleted(self, userID):
|
|
|
|
"""
|
|
|
|
Set userID's slot completed to True
|
|
|
|
|
|
|
|
userID -- ID of user
|
|
|
|
"""
|
|
|
|
slotID = self.getUserSlotID(userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if slotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setSlot(slotID, complete=True)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Console output
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: User {} has completed his play".format(self.matchID, userID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Check all completed
|
|
|
|
total = 0
|
|
|
|
completed = 0
|
|
|
|
for i in range(0,16):
|
2016-09-02 10:41:19 +00:00
|
|
|
if self.slots[i].status == slotStatuses.playing:
|
2016-04-19 17:40:59 +00:00
|
|
|
total+=1
|
2016-09-02 15:45:10 +00:00
|
|
|
if self.slots[i].complete:
|
2016-04-19 17:40:59 +00:00
|
|
|
completed+=1
|
|
|
|
|
|
|
|
if total == completed:
|
|
|
|
self.allPlayersCompleted()
|
|
|
|
|
|
|
|
def allPlayersCompleted(self):
|
|
|
|
"""Cleanup match stuff and send match end packet to everyone"""
|
|
|
|
|
|
|
|
# Reset inProgress
|
|
|
|
self.inProgress = False
|
|
|
|
|
|
|
|
# Reset slots
|
|
|
|
for i in range(0,16):
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[i].user is not None and self.slots[i].status == slotStatuses.playing:
|
2016-09-02 10:41:19 +00:00
|
|
|
self.slots[i].status = slotStatuses.notReady
|
|
|
|
self.slots[i].loaded = False
|
|
|
|
self.slots[i].skip = False
|
|
|
|
self.slots[i].complete = False
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send match update
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send match complete
|
2016-10-04 21:43:02 +00:00
|
|
|
glob.streams.broadcast(self.streamName, serverPackets.matchComplete())
|
|
|
|
|
|
|
|
# Destroy playing stream
|
|
|
|
glob.streams.remove(self.playingStreamName)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Console output
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: Match completed".format(self.matchID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def getUserSlotID(self, userID):
|
|
|
|
"""
|
|
|
|
Get slot ID occupied by userID
|
|
|
|
|
|
|
|
return -- slot id if found, None if user is not in room
|
|
|
|
"""
|
|
|
|
for i in range(0,16):
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[i].user is not None and self.slots[i].user.userID == userID:
|
2016-04-19 17:40:59 +00:00
|
|
|
return i
|
|
|
|
return None
|
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
def userJoin(self, user):
|
2016-04-19 17:40:59 +00:00
|
|
|
"""
|
|
|
|
Add someone to users in match
|
|
|
|
|
|
|
|
userID -- user id of the user
|
|
|
|
return -- True if join success, False if fail (room is full)
|
|
|
|
"""
|
|
|
|
|
2016-09-04 14:24:00 +00:00
|
|
|
# Make sure we're not in this match
|
|
|
|
for i in range(0,16):
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[i].user == user:
|
2016-09-04 14:24:00 +00:00
|
|
|
# Set bugged slot to free
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setSlot(i, slotStatuses.free, 0, None, 0)
|
2016-09-04 14:24:00 +00:00
|
|
|
|
2016-04-19 17:40:59 +00:00
|
|
|
# Find first free slot
|
|
|
|
for i in range(0,16):
|
2016-09-02 10:41:19 +00:00
|
|
|
if self.slots[i].status == slotStatuses.free:
|
2016-04-19 17:40:59 +00:00
|
|
|
# Occupy slot
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setSlot(i, slotStatuses.notReady, 0, user, 0)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send updated match data
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Console output
|
2016-10-04 21:43:02 +00:00
|
|
|
log.info("MPROOM{}: {} joined the room".format(self.matchID, user.username))
|
2016-04-19 17:40:59 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
def userLeft(self, user):
|
2016-04-19 17:40:59 +00:00
|
|
|
"""
|
|
|
|
Remove someone from users in match
|
|
|
|
|
|
|
|
userID -- user if of the user
|
|
|
|
"""
|
|
|
|
# Make sure the user is in room
|
2016-10-04 21:43:02 +00:00
|
|
|
slotID = self.getUserSlotID(user.userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if slotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Set that slot to free
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setSlot(slotID, slotStatuses.free, 0, None, 0)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Check if everyone left
|
|
|
|
if self.countUsers() == 0:
|
|
|
|
# Dispose match
|
|
|
|
glob.matches.disposeMatch(self.matchID)
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: Room disposed".format(self.matchID))
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Check if host left
|
2016-10-04 21:43:02 +00:00
|
|
|
if user.userID == self.hostUserID:
|
2016-04-19 17:40:59 +00:00
|
|
|
# Give host to someone else
|
|
|
|
for i in range(0,16):
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[i].user is not None:
|
|
|
|
self.setHost(self.slots[i].user.userID)
|
2016-04-19 17:40:59 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
# Send updated match data
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Console output
|
2016-10-04 21:43:02 +00:00
|
|
|
log.info("MPROOM{}: {} left the room".format(self.matchID, user.username))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def userChangeSlot(self, userID, newSlotID):
|
|
|
|
"""
|
|
|
|
Change userID slot to newSlotID
|
|
|
|
|
|
|
|
userID -- user that changed slot
|
|
|
|
newSlotID -- slot id of new slot
|
|
|
|
"""
|
|
|
|
# Make sure the user is in room
|
|
|
|
oldSlotID = self.getUserSlotID(userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if oldSlotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Make sure there is no one inside new slot
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[newSlotID].user is not None and self.slots[newSlotID].status != slotStatuses.free:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Get old slot data
|
2016-10-04 21:43:02 +00:00
|
|
|
oldData = dill.copy(self.slots[oldSlotID])
|
2016-10-05 21:28:53 +00:00
|
|
|
|
|
|
|
# Oh no we have a huge meme here.
|
|
|
|
# Get pointer to right token and DON'T copy it
|
|
|
|
oldData.user = glob.tokens.tokens[oldData.user.token]
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Free old slot
|
2016-10-05 21:28:53 +00:00
|
|
|
self.setSlot(oldSlotID, slotStatuses.free, 0, None, 0, False, False, False)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Occupy new slot
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setSlot(newSlotID, oldData.status, oldData.team, oldData.user, oldData.mods)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send updated match data
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Console output
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: {} moved to slot {}".format(self.matchID, userID, newSlotID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def changePassword(self, newPassword):
|
|
|
|
"""
|
|
|
|
Change match password to newPassword
|
|
|
|
|
|
|
|
newPassword -- new password string
|
|
|
|
"""
|
2016-09-02 10:41:19 +00:00
|
|
|
if newPassword != "":
|
2016-10-02 20:48:14 +00:00
|
|
|
self.matchPassword = generalUtils.stringMd5(newPassword)
|
2016-09-02 10:41:19 +00:00
|
|
|
else:
|
|
|
|
self.matchPassword = ""
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send password change to every user in match
|
2016-10-04 21:43:02 +00:00
|
|
|
glob.streams.broadcast(self.streamName, serverPackets.changeMatchPassword(self.matchPassword))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send new match settings too
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Console output
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: Password changed to {}".format(self.matchID, self.matchPassword))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def changeMatchMods(self, mods):
|
|
|
|
"""
|
|
|
|
Set match global mods
|
|
|
|
|
|
|
|
mods -- mods bitwise int thing
|
|
|
|
"""
|
|
|
|
# Set new mods and send update
|
|
|
|
self.mods = mods
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: Mods changed to {}".format(self.matchID, self.mods))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def userHasBeatmap(self, userID, has = True):
|
|
|
|
"""
|
|
|
|
Set no beatmap status for userID
|
|
|
|
|
|
|
|
userID -- ID of user
|
|
|
|
has -- True if has beatmap, false if not
|
|
|
|
"""
|
|
|
|
# Make sure the user is in room
|
|
|
|
slotID = self.getUserSlotID(userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if slotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Set slot
|
|
|
|
self.setSlot(slotID, slotStatuses.noMap if not has else slotStatuses.notReady)
|
|
|
|
|
|
|
|
# Send updates
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def transferHost(self, slotID):
|
|
|
|
"""
|
|
|
|
Transfer host to slotID
|
|
|
|
|
|
|
|
slotID -- ID of slot
|
|
|
|
"""
|
|
|
|
# Make sure there is someone in that slot
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[slotID].user is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Transfer host
|
2016-10-04 21:43:02 +00:00
|
|
|
self.setHost(self.slots[slotID].user.userID)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send updates
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def playerFailed(self, userID):
|
|
|
|
"""
|
|
|
|
Send userID's failed packet to everyone in match
|
|
|
|
|
|
|
|
userID -- ID of user
|
|
|
|
"""
|
|
|
|
# Make sure the user is in room
|
|
|
|
slotID = self.getUserSlotID(userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if slotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Send packet to everyone
|
2016-10-04 21:43:02 +00:00
|
|
|
glob.streams.broadcast(self.playingStreamName, serverPackets.playerFailed(slotID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Console output
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: {} has failed!".format(self.matchID, userID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def invite(self, fro, to):
|
|
|
|
"""
|
|
|
|
Fro invites to in this match.
|
|
|
|
|
|
|
|
fro -- sender userID
|
|
|
|
to -- receiver userID
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Get tokens
|
|
|
|
froToken = glob.tokens.getTokenFromUserID(fro)
|
|
|
|
toToken = glob.tokens.getTokenFromUserID(to)
|
2016-09-02 15:45:10 +00:00
|
|
|
if froToken is None or toToken is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# FokaBot is too busy
|
|
|
|
if to == 999:
|
2016-07-14 10:37:07 +00:00
|
|
|
chat.sendMessage("FokaBot", froToken.username, "I would love to join your match, but I'm busy keeping ripple up and running. Sorry. Beep Boop.")
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
# Send message
|
|
|
|
message = "Come join my multiplayer match: \"[osump://{}/{} {}]\"".format(self.matchID, self.matchPassword.replace(" ", "_"), self.matchName)
|
2016-07-14 10:37:07 +00:00
|
|
|
chat.sendMessage(token=froToken, to=toToken.username, message=message)
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def countUsers(self):
|
|
|
|
"""
|
|
|
|
Return how many players are in that match
|
|
|
|
|
|
|
|
return -- number of users
|
|
|
|
"""
|
|
|
|
c = 0
|
|
|
|
for i in range(0,16):
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[i].user is not None:
|
2016-04-19 17:40:59 +00:00
|
|
|
c+=1
|
|
|
|
return c
|
|
|
|
|
|
|
|
def changeTeam(self, userID):
|
|
|
|
"""
|
|
|
|
Change userID's team
|
|
|
|
|
|
|
|
userID -- id of user
|
|
|
|
"""
|
|
|
|
# Make sure the user is in room
|
|
|
|
slotID = self.getUserSlotID(userID)
|
2016-09-02 15:45:10 +00:00
|
|
|
if slotID is None:
|
2016-04-19 17:40:59 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Update slot and send update
|
2016-09-02 10:41:19 +00:00
|
|
|
newTeam = matchTeams.blue if self.slots[slotID].team == matchTeams.red else matchTeams.red
|
2016-04-19 17:40:59 +00:00
|
|
|
self.setSlot(slotID, None, newTeam)
|
2016-10-04 21:43:02 +00:00
|
|
|
self.sendUpdates()
|
2016-04-19 17:40:59 +00:00
|
|
|
|
2016-10-04 21:43:02 +00:00
|
|
|
def sendUpdates(self):
|
|
|
|
glob.streams.broadcast(self.streamName, serverPackets.updateMatch(self.matchID))
|
2016-10-04 20:10:07 +00:00
|
|
|
glob.streams.broadcast("lobby", serverPackets.updateMatch(self.matchID))
|
2016-04-19 17:40:59 +00:00
|
|
|
|
|
|
|
def checkTeams(self):
|
|
|
|
"""
|
|
|
|
Check if match teams are valid
|
|
|
|
|
|
|
|
return -- True if valid, False if invalid
|
|
|
|
"""
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.matchTeamType != matchTeamTypes.teamVs or self.matchTeamType != matchTeamTypes.tagTeamVs:
|
2016-04-19 17:40:59 +00:00
|
|
|
# Teams are always valid if we have no teams
|
|
|
|
return True
|
|
|
|
|
|
|
|
# We have teams, check if they are valid
|
|
|
|
firstTeam = -1
|
|
|
|
for i in range(0,16):
|
2016-10-04 21:43:02 +00:00
|
|
|
if self.slots[i].user is not None and (self.slots[i].status&slotStatuses.noMap) == 0:
|
2016-04-19 17:40:59 +00:00
|
|
|
if firstTeam == -1:
|
2016-09-02 10:41:19 +00:00
|
|
|
firstTeam = self.slots[i].team
|
|
|
|
elif firstTeam != self.slots[i].team:
|
2016-06-04 10:44:54 +00:00
|
|
|
log.info("MPROOM{}: Teams are valid".format(self.matchID))
|
2016-04-19 17:40:59 +00:00
|
|
|
return True
|
|
|
|
|
2016-06-04 10:44:54 +00:00
|
|
|
log.warning("MPROOM{}: Invalid teams!".format(self.matchID))
|
2016-04-19 17:40:59 +00:00
|
|
|
return False
|
2016-10-04 21:43:02 +00:00
|
|
|
|
|
|
|
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()
|