.BANCHO. pep.py modules

This commit is contained in:
Nyo
2016-05-18 19:12:46 +02:00
parent 993079d2dd
commit 9325578377
74 changed files with 212 additions and 212 deletions

42
objects/banchoConfig.py Normal file
View File

@@ -0,0 +1,42 @@
from objects import glob
from helpers import generalFunctions
class banchoConfig:
"""
Class that loads settings from bancho_settings db table
"""
config = {"banchoMaintenance": False, "freeDirect": True, "menuIcon": "", "loginNotification": ""}
def __init__(self, __loadFromDB = True):
"""
Initialize a banchoConfig object (and load bancho_settings from db)
[__loadFromDB -- if True, load values from db. If False, don't load values. Default: True]
"""
if __loadFromDB:
try:
self.loadSettings()
except:
raise
def loadSettings(self):
"""
(re)load bancho_settings from DB and set values in config array
"""
self.config["banchoMaintenance"] = generalFunctions.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'")["value_int"])
self.config["freeDirect"] = generalFunctions.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'free_direct'")["value_int"])
self.config["menuIcon"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'menu_icon'")["value_string"]
self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"]
def setMaintenance(self, __maintenance):
"""
Turn on/off bancho maintenance mode. Write new value to db too
__maintenance -- if True, turn on maintenance mode. If false, turn it off
"""
self.config["banchoMaintenance"] = __maintenance
glob.db.execute("UPDATE bancho_settings SET value_int = ? WHERE name = 'bancho_maintenance'", [int(__maintenance)])

78
objects/channel.py Normal file
View File

@@ -0,0 +1,78 @@
class channel:
"""
A chat channel
name -- channel name
description -- channel description
connectedUsers -- connected users IDs list
publicRead -- bool
publicWrite -- bool
moderated -- bool
"""
name = ""
description = ""
connectedUsers = []
publicRead = False
publicWrite = False
moderated = False
def __init__(self, __name, __description, __publicRead, __publicWrite):
"""
Create a new chat channel object
__name -- channel name
__description -- channel description
__publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
__publicWrite -- bool, same as public read but relative to write permissions
"""
self.name = __name
self.description = __description
self.publicRead = __publicRead
self.publicWrite = __publicWrite
self.connectedUsers = []
def userJoin(self, __userID):
"""
Add a user to connected users
__userID -- user ID that joined the channel
"""
if __userID not in self.connectedUsers:
self.connectedUsers.append(__userID)
def userPart(self, __userID):
"""
Remove a user from connected users
__userID -- user ID that left the channel
"""
connectedUsers = self.connectedUsers
if __userID in connectedUsers:
connectedUsers.remove(__userID)
def getConnectedUsers(self):
"""
Get connected user IDs list
return -- connectedUsers list
"""
return self.connectedUsers
def getConnectedUsersCount(self):
"""
Count connected users
return -- connected users number
"""
return len(self.connectedUsers)

40
objects/channelList.py Normal file
View File

@@ -0,0 +1,40 @@
from objects import glob
from objects import channel
class channelList:
"""
Channel list
channels -- dictionary. key: channel name, value: channel object
"""
channels = {}
def loadChannels(self):
"""
Load chat channels from db and add them to channels dictionary
"""
# Get channels from DB
channels = glob.db.fetchAll("SELECT * FROM bancho_channels")
# Add each channel if needed
for i in channels:
if i["name"] not in self.channels:
publicRead = True if i["public_read"] == 1 else False
publicWrite = True if i["public_write"] == 1 else False
self.addChannel(i["name"], i["description"], publicRead, publicWrite)
def addChannel(self, __name, __description, __publicRead, __publicWrite):
"""
Add a channel object to channels dictionary
__name -- channel name
__description -- channel description
__publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
__publicWrite -- bool, same as public read but relative to write permissions
"""
self.channels[__name] = channel.channel(__name, __description, __publicRead, __publicWrite)

55
objects/fokabot.py Normal file
View File

@@ -0,0 +1,55 @@
"""FokaBot related functions"""
from helpers import userHelper
from objects import glob
from constants import actions
from constants import serverPackets
from constants import fokabotCommands
def connect():
"""Add FokaBot to connected users and send userpanel/stats packet to everyone"""
token = glob.tokens.addToken(999)
token.actionID = actions.idle
glob.tokens.enqueueAll(serverPackets.userPanel(999))
glob.tokens.enqueueAll(serverPackets.userStats(999))
def disconnect():
"""Remove FokaBot from connected users"""
glob.tokens.deleteToken(glob.tokens.getTokenFromUserID(999))
def fokabotResponse(fro, chan, message):
"""
Check if a message has triggered fokabot (and return its response)
fro -- sender username (for permissions stuff with admin commands)
chan -- channel name
message -- message
return -- fokabot's response string or False
"""
for i in fokabotCommands.commands:
# Loop though all commands
if i["trigger"] in message:
# message has triggered a command
# Make sure the user has right permissions
if i["minRank"] > 1:
# Get rank from db only if minrank > 1, so we save some CPU
if userHelper.getRankPrivileges(userHelper.getID(fro)) < i["minRank"]:
return False
# Check argument number
message = message.split(" ")
if i["syntax"] != "" and len(message) <= len(i["syntax"].split(" ")):
return "Wrong syntax: {} {}".format(i["trigger"], i["syntax"])
# Return response or execute callback
if i["callback"] == None:
return i["response"]
else:
return i["callback"](fro, chan, message[1:])
# No commands triggered
return False

16
objects/glob.py Normal file
View File

@@ -0,0 +1,16 @@
"""Global objects and variables"""
from objects import tokenList
from objects import channelList
from objects import matchList
VERSION = "0.9"
db = None
conf = None
banchoConf = None
tokens = tokenList.tokenList()
channels = channelList.channelList()
matches = matchList.matchList()
memes = True
restarting = False

656
objects/match.py Normal file
View File

@@ -0,0 +1,656 @@
# TODO: Enqueue all
from constants import gameModes
from constants import matchScoringTypes
from constants import matchTeamTypes
from constants import matchModModes
from constants import slotStatuses
from objects import glob
from helpers import consoleHelper
from constants import bcolors
from constants import serverPackets
from constants import dataTypes
from constants import matchTeams
class match:
"""Multiplayer match object"""
matchID = 0
inProgress = False
mods = 0
matchName = ""
matchPassword = ""
beatmapName = ""
beatmapID = 0
beatmapMD5 = ""
slots = [] # list of dictionaries {"status": 0, "team": 0, "userID": -1, "mods": 0, "loaded": False, "skip": False, "complete": False}
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):
"""
Create a new match object
__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
"""
self.matchID = __matchID
self.inProgress = False
self.mods = 0
self.matchName = __matchName
self.matchPassword = __matchPassword
self.beatmapID = __beatmapID
self.beatmapName = __beatmapName
self.beatmapMD5 = __beatmapMD5
self.hostUserID = __hostUserID
self.gameMode = __gameMode
self.matchScoringTypes = matchScoringTypes.score # default values
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):
self.slots.append({"status": slotStatuses.free, "team": 0, "userID": -1, "mods": 0, "loaded": False, "skip": False, "complete": False})
def getMatchData(self):
"""
Return binary match data structure for packetHelper
"""
# General match info
struct = [
[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],
]
# Slots status IDs, always 16 elements
for i in range(0,16):
struct.append([self.slots[i]["status"], dataTypes.byte])
# Slot teams, always 16 elements
for i in range(0,16):
struct.append([self.slots[i]["team"], dataTypes.byte])
# Slot user ID. Write only if slot is occupied
for i in range(0,16):
uid = self.slots[i]["userID"]
if uid > -1:
struct.append([uid, dataTypes.uInt32])
# Other match data
struct.extend([
[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 self.matchModMode == matchModModes.freeMod:
for i in range(0,16):
struct.append([self.slots[i]["mods"], dataTypes.uInt32])
# Seed idk
struct.append([self.seed, dataTypes.uInt32])
return struct
def setHost(self, newHost):
"""
Set room host to newHost and send him host packet
newHost -- new host userID
"""
self.hostUserID = newHost
# Send host packet to new host
token = glob.tokens.getTokenFromUserID(newHost)
if token != None:
token.enqueue(serverPackets.matchTransferHost())
consoleHelper.printColored("> MPROOM{}: {} is now the host".format(self.matchID, newHost), bcolors.BLUE)
def setSlot(self, slotID, slotStatus = None, slotTeam = None, slotUserID = None, slotMods = None, slotLoaded = None, slotSkip = None, slotComplete = None):
"""
Set a slot to a specific userID and status
slotID -- id of that slot (0-15)
slotStatus -- see slotStatuses.py
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 slotStatus != None:
self.slots[slotID]["status"] = slotStatus
if slotTeam != None:
self.slots[slotID]["team"] = slotTeam
if slotUserID != None:
self.slots[slotID]["userID"] = slotUserID
if slotMods != None:
self.slots[slotID]["mods"] = slotMods
if slotLoaded != None:
self.slots[slotID]["loaded"] = slotLoaded
if slotSkip != None:
self.slots[slotID]["skip"] = slotSkip
if slotComplete != None:
self.slots[slotID]["complete"] = slotComplete
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
self.setSlot(slotID, None, None, None, mods)
self.sendUpdate()
consoleHelper.printColored("> MPROOM{}: Slot{} mods changed to {}".format(self.matchID, slotID, mods), bcolors.BLUE)
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
oldStatus = self.slots[slotID]["status"]
if oldStatus == slotStatuses.ready:
newStatus = slotStatuses.notReady
else:
newStatus = slotStatuses.ready
self.setSlot(slotID, newStatus, None, None, None)
self.sendUpdate()
consoleHelper.printColored("> MPROOM{}: Slot{} changed ready status to {}".format(self.matchID, slotID, self.slots[slotID]["status"]), bcolors.BLUE)
def toggleSlotLock(self, slotID):
"""
Lock a slot
Same as calling setSlot and then sendUpdate
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
if self.slots[slotID]["status"] == slotStatuses.locked:
newStatus = slotStatuses.free
else:
newStatus = slotStatuses.locked
# Set new slot status
self.setSlot(slotID, newStatus, 0, -1, 0)
if token != None:
# Send updated settings to kicked user, so he returns to lobby
token.enqueue(serverPackets.updateMatch(self.matchID))
# Send updates to everyone else
self.sendUpdate()
consoleHelper.printColored("> MPROOM{}: Slot{} {}".format(self.matchID, slotID, "locked" if newStatus == slotStatuses.locked else "unlocked"), bcolors.BLUE)
def playerLoaded(self, userID):
"""
Set a player loaded status to True
userID -- ID of user
"""
slotID = self.getUserSlotID(userID)
if slotID == None:
return
# Set loaded to True
self.slots[slotID]["loaded"] = True
consoleHelper.printColored("> MPROOM{}: User {} loaded".format(self.matchID, userID), bcolors.BLUE)
# Check all loaded
total = 0
loaded = 0
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.playing:
total+=1
if self.slots[i]["loaded"] == True:
loaded+=1
if total == loaded:
self.allPlayersLoaded()
def allPlayersLoaded(self):
"""Send allPlayersLoaded packet to every playing usr in match"""
for i in range(0,16):
if self.slots[i]["userID"] > -1 and self.slots[i]["status"] == slotStatuses.playing:
token = glob.tokens.getTokenFromUserID(self.slots[i]["userID"])
if token != None:
token.enqueue(serverPackets.allPlayersLoaded())
consoleHelper.printColored("> MPROOM{}: All players loaded! Corrispondere iniziare in 3...".format(self.matchID), bcolors.BLUE)
def playerSkip(self, userID):
"""
Set a player skip status to True
userID -- ID of user
"""
slotID = self.getUserSlotID(userID)
if slotID == None:
return
# Set skip to True
self.slots[slotID]["skip"] = True
consoleHelper.printColored("> MPROOM{}: User {} skipped".format(self.matchID, userID), bcolors.BLUE)
# Send skip packet to every playing useR
for i in range(0,16):
uid = self.slots[i]["userID"]
if self.slots[i]["status"] == slotStatuses.playing and uid > -1:
token = glob.tokens.getTokenFromUserID(uid)
if token != None:
print("Enqueueueue {}".format(uid))
token.enqueue(serverPackets.playerSkipped(uid))
# Check all skipped
total = 0
skipped = 0
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.playing:
total+=1
if self.slots[i]["skip"] == True:
skipped+=1
if total == skipped:
self.allPlayersSkipped()
def allPlayersSkipped(self):
"""Send allPlayersSkipped packet to every playing usr in match"""
for i in range(0,16):
if self.slots[i]["userID"] > -1 and self.slots[i]["status"] == slotStatuses.playing:
token = glob.tokens.getTokenFromUserID(self.slots[i]["userID"])
if token != None:
token.enqueue(serverPackets.allPlayersSkipped())
consoleHelper.printColored("> MPROOM{}: All players skipped!".format(self.matchID), bcolors.BLUE)
def playerCompleted(self, userID):
"""
Set userID's slot completed to True
userID -- ID of user
"""
slotID = self.getUserSlotID(userID)
if slotID == None:
return
self.setSlot(slotID, None, None, None, None, None, None, True)
# Console output
consoleHelper.printColored("> MPROOM{}: User {} has completed".format(self.matchID, userID), bcolors.BLUE)
# Check all completed
total = 0
completed = 0
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.playing:
total+=1
if self.slots[i]["complete"] == True:
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):
if self.slots[i]["userID"] > -1 and self.slots[i]["status"] == slotStatuses.playing:
self.slots[i]["status"] = slotStatuses.notReady
self.slots[i]["loaded"] = False
self.slots[i]["skip"] = False
self.slots[i]["complete"] = False
# Send match update
self.sendUpdate()
# Send match complete
for i in range(0,16):
if self.slots[i]["userID"] > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i]["userID"])
if token != None:
token.enqueue(serverPackets.matchComplete())
# Console output
consoleHelper.printColored("> MPROOM{}: Match completed".format(self.matchID), bcolors.BLUE)
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):
if self.slots[i]["userID"] == userID:
return i
return None
def userJoin(self, userID):
"""
Add someone to users in match
userID -- user id of the user
return -- True if join success, False if fail (room is full)
"""
# Find first free slot
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.free:
# Occupy slot
self.setSlot(i, slotStatuses.notReady, 0, userID, 0)
# Send updated match data
self.sendUpdate()
# Console output
consoleHelper.printColored("> MPROOM{}: {} joined the room".format(self.matchID, userID), bcolors.BLUE)
return True
return False
def userLeft(self, userID):
"""
Remove someone from users in match
userID -- user if of the user
"""
# Make sure the user is in room
slotID = self.getUserSlotID(userID)
if slotID == None:
return
# Set that slot to free
self.setSlot(slotID, slotStatuses.free, 0, -1, 0)
# Check if everyone left
if self.countUsers() == 0:
# Dispose match
glob.matches.disposeMatch(self.matchID)
consoleHelper.printColored("> MPROOM{}: Room disposed".format(self.matchID), bcolors.BLUE)
return
# Check if host left
if userID == self.hostUserID:
# Give host to someone else
for i in range(0,16):
uid = self.slots[i]["userID"]
if uid > -1:
self.setHost(uid)
break
# Send updated match data
self.sendUpdate()
# Console output
consoleHelper.printColored("> MPROOM{}: {} left the room".format(self.matchID, userID), bcolors.BLUE)
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)
if oldSlotID == None:
return
# Make sure there is no one inside new slot
if self.slots[newSlotID]["userID"] > -1:
return
# Get old slot data
oldData = self.slots[oldSlotID].copy()
# Free old slot
self.setSlot(oldSlotID, slotStatuses.free, 0, -1, 0)
# Occupy new slot
self.setSlot(newSlotID, oldData["status"], oldData["team"], userID, oldData["mods"])
# Send updated match data
self.sendUpdate()
# Console output
consoleHelper.printColored("> MPROOM{}: {} moved to slot {}".format(self.matchID, userID, newSlotID), bcolors.BLUE)
def changePassword(self, newPassword):
"""
Change match password to newPassword
newPassword -- new password string
"""
self.matchPassword = newPassword
# Send password change to every user in match
for i in range(0,16):
if self.slots[i]["userID"] > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i]["userID"])
if token != None:
token.enqueue(serverPackets.changeMatchPassword(self.matchPassword))
# Send new match settings too
self.sendUpdate()
# Console output
consoleHelper.printColored("> MPROOM{}: Password changed to {}".format(self.matchID, self.matchPassword), bcolors.BLUE)
def changeMatchMods(self, mods):
"""
Set match global mods
mods -- mods bitwise int thing
"""
# Set new mods and send update
self.mods = mods
self.sendUpdate()
consoleHelper.printColored("> MPROOM{}: Mods changed to {}".format(self.matchID, self.mods), bcolors.BLUE)
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)
if slotID == None:
return
# Set slot
self.setSlot(slotID, slotStatuses.noMap if not has else slotStatuses.notReady)
# Send updates
self.sendUpdate()
def transferHost(self, slotID):
"""
Transfer host to slotID
slotID -- ID of slot
"""
# Make sure there is someone in that slot
uid = self.slots[slotID]["userID"]
if uid == -1:
return
# Transfer host
self.setHost(uid)
# Send updates
self.sendUpdate()
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)
if slotID == None:
return
# Send packet to everyone
for i in range(0,16):
uid = self.slots[i]["userID"]
if uid > -1:
token = glob.tokens.getTokenFromUserID(uid)
if token != None:
token.enqueue(serverPackets.playerFailed(slotID))
# Console output
consoleHelper.printColored("> MPROOM{}: {} has failed!".format(self.matchID, userID), bcolors.BLUE)
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)
if froToken == None or toToken == None:
return
# FokaBot is too busy
if to == 999:
froToken.enqueue(serverPackets.sendMessage("FokaBot", froToken.username, "I would love to join your match, but I'm busy keeping ripple up and running. Sorry. Beep Boop."))
# Send message
message = "Come join my multiplayer match: \"[osump://{}/{} {}]\"".format(self.matchID, self.matchPassword.replace(" ", "_"), self.matchName)
toToken.enqueue(serverPackets.sendMessage(froToken.username, toToken.username, message))
def countUsers(self):
"""
Return how many players are in that match
return -- number of users
"""
c = 0
for i in range(0,16):
if self.slots[i]["userID"] > -1:
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)
if slotID == None:
return
# Update slot and send update
newTeam = matchTeams.blue if self.slots[slotID]["team"] == matchTeams.red else matchTeams.red
self.setSlot(slotID, None, newTeam)
self.sendUpdate()
def sendUpdate(self):
# Send to users in room
for i in range(0,16):
if self.slots[i]["userID"] > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i]["userID"])
if token != None:
token.enqueue(serverPackets.updateMatch(self.matchID))
# Send to users in lobby
for i in glob.matches.usersInLobby:
token = glob.tokens.getTokenFromUserID(i)
if token != None:
token.enqueue(serverPackets.updateMatch(self.matchID))
def checkTeams(self):
"""
Check if match teams are valid
return -- True if valid, False if invalid
"""
if match.matchTeamType != matchTeamTypes.teamVs or matchTeamTypes != matchTeamTypes.tagTeamVs:
# 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):
if self.slots[i]["userID"] > -1 and (self.slots[i]["status"]&slotStatuses.noMap) == 0:
if firstTeam == -1:
firstTeam = self.slots[i]["team"]
elif firstTeam != self.slots[i]["teams"]:
consoleHelper.printColored("> MPROOM{}: Teams are valid".format(self.matchID), bcolors.BLUE)
return True
consoleHelper.printColored("> MPROOM{}: Invalid teams!".format(self.matchID), bcolors.RED)
return False

80
objects/matchList.py Normal file
View File

@@ -0,0 +1,80 @@
from objects import match
from objects import glob
from constants import serverPackets
class matchList:
matches = {}
usersInLobby = []
lastID = 1
def __init__(self):
"""Initialize a matchList object"""
self.matches = {}
self.usersInLobby = []
self.lastID = 1
def createMatch(self, __matchName, __matchPassword, __beatmapID, __beatmapName, __beatmapMD5, __gameMode, __hostUserID):
"""
Add a new match to matches list
__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 who created the match
return -- match ID
"""
# Add a new match to matches list
matchID = self.lastID
self.lastID+=1
self.matches[matchID] = match.match(matchID, __matchName, __matchPassword, __beatmapID, __beatmapName, __beatmapMD5, __gameMode, __hostUserID)
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):
"""
Destroy match object with id = __matchID
__matchID -- ID of match to dispose
"""
# Make sure the match exists
if __matchID not in self.matches:
return
# Remove match object
self.matches.pop(__matchID)
# Send match dispose packet to everyone in lobby
for i in self.usersInLobby:
token = glob.tokens.getTokenFromUserID(i)
if token != None:
token.enqueue(serverPackets.disposeMatch(__matchID))

227
objects/osuToken.py Normal file
View File

@@ -0,0 +1,227 @@
import uuid
from constants import actions
from constants import gameModes
from helpers import userHelper
import time
from helpers import consoleHelper
from constants import bcolors
from constants import serverPackets
from events import logoutEvent
class token:
"""Osu Token object
token -- token string
userID -- userID associated to that token
username -- username relative to userID (cache)
rank -- rank (permissions) relative to userID (cache)
actionID -- current user action (see actions.py)
actionText -- current user action text
actionMd5 -- md5 relative to user action
actionMods -- current acton mods
gameMode -- current user game mode
location -- [latitude,longitude]
queue -- packets queue
joinedChannels -- list. Contains joined channel names
spectating -- userID of spectating user. 0 if not spectating.
spectators -- list. Contains userIDs of spectators
country -- osu country code. Use countryHelper to convert from letter country code to osu country code
pingTime -- latest packet received UNIX time
loginTime -- login UNIX time
"""
token = ""
userID = 0
username = ""
rank = 0
actionID = actions.idle
actionText = ""
actionMd5 = ""
actionMods = 0
gameMode = gameModes.std
country = 0
location = [0,0]
queue = bytes()
joinedChannels = []
spectating = 0
spectators = []
pingTime = 0
loginTime = 0
awayMessage = ""
matchID = -1
def __init__(self, __userID, __token = None):
"""
Create a token object and set userID and token
__userID -- user associated to this token
__token -- if passed, set token to that value
if not passed, token will be generated
"""
# Set stuff
self.userID = __userID
self.username = userHelper.getUsername(self.userID)
self.rank = userHelper.getRankPrivileges(self.userID)
self.loginTime = int(time.time())
self.pingTime = self.loginTime
# Default variables
self.spectators = []
self.spectating = 0
self.location = [0,0]
self.joinedChannels = []
self.actionID = actions.idle
self.actionText = ""
self.actionMods = 0
self.gameMode = gameModes.std
self.awayMessage = ""
self.matchID = -1
# Generate/set token
if __token != None:
self.token = __token
else:
self.token = str(uuid.uuid4())
def enqueue(self, __bytes):
"""
Add bytes (packets) to queue
__bytes -- (packet) bytes to enqueue
"""
self.queue += __bytes
def resetQueue(self):
"""Resets the queue. Call when enqueued packets have been sent"""
self.queue = bytes()
def joinChannel(self, __channel):
"""Add __channel to joined channels list
__channel -- channel name"""
if __channel not in self.joinedChannels:
self.joinedChannels.append(__channel)
def partChannel(self, __channel):
"""Remove __channel from joined channels list
__channel -- channel name"""
if __channel in self.joinedChannels:
self.joinedChannels.remove(__channel)
def setLocation(self, __location):
"""Set location (latitude and longitude)
__location -- [latitude, longitude]"""
self.location = __location
def getLatitude(self):
"""Get latitude
return -- latitude"""
return self.location[0]
def getLongitude(self):
"""Get longitude
return -- longitude"""
return self.location[1]
def startSpectating(self, __userID):
"""Set the spectating user to __userID
__userID -- target userID"""
self.spectating = __userID
def stopSpectating(self):
"""Set the spectating user to 0, aka no user"""
self.spectating = 0
def addSpectator(self, __userID):
"""Add __userID to our spectators
userID -- new spectator userID"""
# Add userID to spectators if not already in
if __userID not in self.spectators:
self.spectators.append(__userID)
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):
"""Set country to __countryID
__countryID -- numeric country ID. See countryHelper.py"""
self.country = __countryID
def getCountry(self):
"""Get numeric country ID
return -- numeric country ID. See countryHelper.py"""
return self.country
def updatePingTime(self):
"""Update latest ping time"""
self.pingTime = int(time.time())
def setAwayMessage(self, __awayMessage):
"""Set a new away message"""
self.awayMessage = __awayMessage
def joinMatch(self, __matchID):
"""
Set match to matchID
__matchID -- new match ID
"""
self.matchID = __matchID
def partMatch(self):
"""Set match to -1"""
self.matchID = -1
def kick(self):
"""Kick this user from the server"""
# Send packet to target
consoleHelper.printColored("> {} has been disconnected. (kick)".format(self.username), bcolors.YELLOW)
self.enqueue(serverPackets.notification("You have been kicked from the server. Please login again."))
self.enqueue(serverPackets.loginFailed())
# Logout event
logoutEvent.handle(self, None)

165
objects/tokenList.py Normal file
View File

@@ -0,0 +1,165 @@
from objects import osuToken
import time
import threading
from events import logoutEvent
class tokenList:
"""
List of connected osu tokens
tokens -- dictionary. key: token string, value: token object
"""
tokens = {}
def addToken(self, __userID):
"""
Add a token object to tokens list
__userID -- user id associated to that token
return -- token object
"""
newToken = osuToken.token(__userID)
self.tokens[newToken.token] = newToken
return newToken
def deleteToken(self, __token):
"""
Delete a token from token list if it exists
__token -- token string
"""
if __token in self.tokens:
self.tokens.pop(__token)
def getUserIDFromToken(self, __token):
"""
Get user ID from a token
__token -- token to find
return: false if not found, userID if found
"""
# Make sure the token exists
if __token not in self.tokens:
return False
# Get userID associated to that token
return self.tokens[__token].userID
def getTokenFromUserID(self, __userID):
"""
Get token from a user ID
__userID -- user ID to find
return -- False if not found, token object if found
"""
# Make sure the token exists
for _, value in self.tokens.items():
if value.userID == __userID:
return value
# Return none if not found
return None
def getTokenFromUsername(self, __username):
"""
Get token from a username
__username -- username to find
return -- False if not found, token object if found
"""
# lowercase
who = __username.lower()
# Make sure the token exists
for _, value in self.tokens.items():
if value.username.lower() == who:
return value
# Return none if not found
return None
def deleteOldTokens(self, __userID):
"""
Delete old userID's tokens if found
__userID -- tokens associated to this user will be deleted
"""
# Delete older tokens
for key, value in self.tokens.items():
if value.userID == __userID:
# Delete this token from the dictionary
self.tokens.pop(key)
# break or items() function throws errors
break
def multipleEnqueue(self, __packet, __who, __but = False):
"""
Enqueue a packet to multiple users
__packet -- packet bytes to enqueue
__who -- userIDs array
__but -- if True, enqueue to everyone but users in __who array
"""
for _, value in self.tokens.items():
shouldEnqueue = False
if value.userID in __who and not __but:
shouldEnqueue = True
elif value.userID not in __who and __but:
shouldEnqueue = True
if shouldEnqueue:
value.enqueue(__packet)
def enqueueAll(self, __packet):
"""
Enqueue packet(s) to every connected user
__packet -- packet bytes to enqueue
"""
for _, value in self.tokens.items():
value.enqueue(__packet)
def usersTimeoutCheckLoop(self, __timeoutTime = 100, __checkTime = 100):
"""
Deletes all timed out users.
If called once, will recall after __checkTime seconds and so on, forever
CALL THIS FUNCTION ONLY ONCE!
__timeoutTime - seconds of inactivity required to disconnect someone (Default: 100)
__checkTime - seconds between loops (Default: 100)
"""
timedOutTokens = [] # timed out users
timeoutLimit = time.time()-__timeoutTime
for key, value in self.tokens.items():
# Check timeout (fokabot is ignored)
if value.pingTime < timeoutLimit and value.userID != 999:
# That user has timed out, add to disconnected tokens
# We can't delete it while iterating or items() throws an error
timedOutTokens.append(key)
# Delete timed out users from self.tokens
# i is token string (dictionary key)
for i in timedOutTokens:
logoutEvent.handle(self.tokens[i], None)
# Schedule a new check (endless loop)
threading.Timer(__checkTime, self.usersTimeoutCheckLoop, [__timeoutTime, __checkTime]).start()