.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

143
constants/clientPackets.py Normal file
View File

@@ -0,0 +1,143 @@
""" Contains functions used to read specific client packets from byte stream """
from constants import dataTypes
from helpers import packetHelper
from constants import slotStatuses
""" General packets """
def userActionChange(stream):
return packetHelper.readPacketData(stream,
[
["actionID", dataTypes.byte],
["actionText", dataTypes.string],
["actionMd5", dataTypes.string],
["actionMods", dataTypes.uInt32],
["gameMode", dataTypes.byte]
])
""" Client chat packets """
def sendPublicMessage(stream):
return packetHelper.readPacketData(stream,
[
["unknown", dataTypes.string],
["message", dataTypes.string],
["to", dataTypes.string]
])
def sendPrivateMessage(stream):
return packetHelper.readPacketData(stream,
[
["unknown", dataTypes.string],
["message", dataTypes.string],
["to", dataTypes.string],
["unknown2", dataTypes.uInt32]
])
def setAwayMessage(stream):
return packetHelper.readPacketData(stream,
[
["unknown", dataTypes.string],
["awayMessage", dataTypes.string]
])
def channelJoin(stream):
return packetHelper.readPacketData(stream,[["channel", dataTypes.string]])
def channelPart(stream):
return packetHelper.readPacketData(stream,[["channel", dataTypes.string]])
def addRemoveFriend(stream):
return packetHelper.readPacketData(stream, [["friendID", dataTypes.sInt32]])
""" SPECTATOR PACKETS """
def startSpectating(stream):
return packetHelper.readPacketData(stream,[["userID", dataTypes.sInt32]])
""" MULTIPLAYER PACKETS """
def matchSettings(stream):
# Data to return, will be merged later
data = []
# Some settings
struct = [
["matchID", dataTypes.uInt16],
["inProgress", dataTypes.byte],
["unknown", dataTypes.byte],
["mods", dataTypes.uInt32],
["matchName", dataTypes.string],
["matchPassword", dataTypes.string],
["beatmapName", dataTypes.string],
["beatmapID", dataTypes.uInt32],
["beatmapMD5", dataTypes.string]
]
# Slot statuses (not used)
for i in range(0,16):
struct.append(["slot{}Status".format(str(i)), dataTypes.byte])
# Slot statuses (not used)
for i in range(0,16):
struct.append(["slot{}Team".format(str(i)), dataTypes.byte])
# Read first part
data.append(packetHelper.readPacketData(stream, struct))
# Skip userIDs because fuck
start = 7+2+1+1+4+4+16+16+len(data[0]["matchName"])+len(data[0]["matchPassword"])+len(data[0]["beatmapMD5"])+len(data[0]["beatmapName"])
start += 1 if (data[0]["matchName"] == "") else 2
start += 1 if (data[0]["matchPassword"] == "") else 2
start += 2 # If beatmap name and MD5 don't change, the client sends \x0b\x00 istead of \x00 only, so always add 2. ...WHY!
start += 2
for i in range(0,16):
s = data[0]["slot{}Status".format(str(i))]
if s != slotStatuses.free and s != slotStatuses.locked:
start += 4
# Other settings
struct = [
["hostUserID", dataTypes.sInt32],
["gameMode", dataTypes.byte],
["scoringType", dataTypes.byte],
["teamType", dataTypes.byte],
["freeMods", dataTypes.byte],
]
# Read last part
data.append(packetHelper.readPacketData(stream[start:], struct, False))
# Mods if freemod (not used)
#if data[1]["freeMods"] == 1:
result = {}
for i in data:
result.update(i)
return result
def createMatch(stream):
return matchSettings(stream)
def changeMatchSettings(stream):
return matchSettings(stream)
def changeSlot(stream):
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
def joinMatch(stream):
return packetHelper.readPacketData(stream, [["matchID", dataTypes.uInt32], ["password", dataTypes.string]])
def changeMods(stream):
return packetHelper.readPacketData(stream, [["mods", dataTypes.uInt32]])
def lockSlot(stream):
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
def transferHost(stream):
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
def matchInvite(stream):
return packetHelper.readPacketData(stream, [["userID", dataTypes.uInt32]])

12
constants/dataTypes.py Normal file
View File

@@ -0,0 +1,12 @@
"""Bancho packets data types"""
#TODO: Uppercase, maybe?
byte = 0
uInt16 = 1
sInt16 = 2
uInt32 = 3
sInt32 = 4
uInt64 = 5
sInt64 = 6
string = 7
ffloat = 8 # because float is a keyword
bbytes = 9

58
constants/exceptions.py Normal file
View File

@@ -0,0 +1,58 @@
"""Bancho exceptions"""
# TODO: Prints in exceptions
class loginFailedException(Exception):
pass
class loginBannedException(Exception):
pass
class tokenNotFoundException(Exception):
pass
class channelNoPermissionsException(Exception):
pass
class channelUnknownException(Exception):
pass
class channelModeratedException(Exception):
pass
class noAdminException(Exception):
pass
class commandSyntaxException(Exception):
pass
class banchoConfigErrorException(Exception):
pass
class banchoMaintenanceException(Exception):
pass
class moderatedPMException(Exception):
pass
class userNotFoundException(Exception):
pass
class alreadyConnectedException(Exception):
pass
class stopSpectating(Exception):
pass
class matchWrongPasswordException(Exception):
pass
class matchNotFoundException(Exception):
pass
class matchJoinErrorException(Exception):
pass
class matchCreateError(Exception):
pass
class banchoRestartingException(Exception):
pass

View File

@@ -0,0 +1,402 @@
from objects import fokabot
import random
from objects import glob
from constants import serverPackets
from constants import exceptions
from helpers import userHelper
import time
from helpers import systemHelper
"""
Commands callbacks
Must have fro, chan and messages as arguments
fro -- name of who triggered the command
chan -- channel where the message was sent
message -- list containing arguments passed from the message
[0] = first argument
[1] = second argument
. . .
return the message or **False** if there's no response by the bot
"""
def faq(fro, chan, message):
if message[0] == "rules":
return "Please make sure to check (Ripple's rules)[http://ripple.moe/?p=23]."
elif message[0] == "rules":
return "Please make sure to check (Ripple's rules)[http://ripple.moe/?p=23]."
elif message[0] == "swearing":
return "Please don't abuse swearing"
elif message[0] == "spam":
return "Please don't spam"
elif message[0] == "offend":
return "Please don't offend other players"
elif message[0] == "github":
return "(Ripple's Github page!)[https://github.com/osuripple/ripple]"
elif message[0] == "discord":
return "(Join Ripple's Discord!)[https://discord.gg/0rJcZruIsA6rXuIx]"
elif message[0] == "blog":
return "You can find the latest Ripple news on the (blog)[https://ripple.moe/blog/]!"
elif message[0] == "changelog":
return "Check the (changelog)[https://ripple.moe/index.php?p=17] !"
elif message[0] == "status":
return "Check the server status (here!)[https://ripple.moe/index.php?p=27]"
def roll(fro, chan, message):
maxPoints = 100
if len(message) >= 1:
if message[0].isdigit() == True and int(message[0]) > 0:
maxPoints = int(message[0])
points = random.randrange(0,maxPoints)
return "{} rolls {} points!".format(fro, str(points))
def ask(fro, chan, message):
return random.choice(["yes", "no", "maybe"])
def alert(fro, chan, message):
glob.tokens.enqueueAll(serverPackets.notification(' '.join(message[:])))
return False
def moderated(fro, chan, message):
try:
# Make sure we are in a channel and not PM
if chan.startswith("#") == False:
raise exceptions.moderatedPMException
# Get on/off
enable = True
if len(message) >= 1:
if message[0] == "off":
enable = False
# Turn on/off moderated mode
glob.channels.channels[chan].moderated = enable
return "This channel is {} in moderated mode!".format("now" if enable else "no longer")
except exceptions.moderatedPMException:
return "You are trying to put a private chat in moderated mode. Are you serious?!? You're fired."
def kickAll(fro, chan, message):
# Kick everyone but mods/admins
toKick = []
for key, value in glob.tokens.tokens.items():
if value.rank < 3:
toKick.append(key)
# Loop though users to kick (we can't change dictionary size while iterating)
for i in toKick:
if i in glob.tokens.tokens:
glob.tokens.tokens[i].kick()
return "Whoops! Rip everyone."
def kick(fro, chan, message):
# Get parameters
target = message[0].replace("_", " ")
# Get target token and make sure is connected
targetToken = glob.tokens.getTokenFromUsername(target)
if targetToken == None:
return "{} is not online".format(target)
# Kick user
targetToken.kick()
# Bot response
return "{} has been kicked from the server.".format(target)
def fokabotReconnect(fro, chan, message):
# Check if fokabot is already connected
if glob.tokens.getTokenFromUserID(999) != None:
return"Fokabot is already connected to Bancho"
# Fokabot is not connected, connect it
fokabot.connect()
return False
def silence(fro, chan, message):
for i in message:
i = i.lower()
target = message[0].replace("_", " ")
amount = message[1]
unit = message[2]
reason = ' '.join(message[3:])
# Get target user ID
targetUserID = userHelper.getID(target)
# Make sure the user exists
if targetUserID == False:
return "{}: user not found".format(target)
# Calculate silence seconds
if unit == 's':
silenceTime = int(amount)
elif unit == 'm':
silenceTime = int(amount)*60
elif unit == 'h':
silenceTime = int(amount)*3600
elif unit == 'd':
silenceTime = int(amount)*86400
else:
return "Invalid time unit (s/m/h/d)."
# Max silence time is 7 days
if silenceTime > 604800:
return "Invalid silence time. Max silence time is 7 days."
# Calculate silence end time
endTime = int(time.time())+silenceTime
# Update silence end in db
userHelper.silence(targetUserID, endTime, reason)
# Send silence packet to target if he's connected
targetToken = glob.tokens.getTokenFromUsername(target)
if targetToken != None:
targetToken.enqueue(serverPackets.silenceEndTime(silenceTime))
return "{} has been silenced for the following reason: {}".format(target, reason)
def removeSilence(fro, chan, message):
# Get parameters
for i in message:
i = i.lower()
target = message[0].replace("_", " ")
# Make sure the user exists
targetUserID = userHelper.getID(target)
if targetUserID == False:
return "{}: user not found".format(target)
# Reset user silence time and reason in db
userHelper.silence(targetUserID, 0, "")
# Send new silence end packet to user if he's online
targetToken = glob.tokens.getTokenFromUsername(target)
if targetToken != None:
targetToken.enqueue(serverPackets.silenceEndTime(0))
return "{}'s silence reset".format(target)
def ban(fro, chan, message):
# Get parameters
for i in message:
i = i.lower()
target = message[0].replace("_", " ")
# Make sure the user exists
targetUserID = userHelper.getID(target)
if targetUserID == False:
return "{}: user not found".format(target)
# Set allowed to 0
userHelper.setAllowed(targetUserID, 0)
# Send ban packet to the user if he's online
targetToken = glob.tokens.getTokenFromUsername(target)
if targetToken != None:
targetToken.enqueue(serverPackets.loginBanned())
return "RIP {}. You will not be missed.".format(target)
def unban(fro, chan, message):
# Get parameters
for i in message:
i = i.lower()
target = message[0].replace("_", " ")
# Make sure the user exists
targetUserID = userHelper.getID(target)
if targetUserID == False:
return "{}: user not found".format(target)
# Set allowed to 1
userHelper.setAllowed(targetUserID, 1)
return "Welcome back {}!".format(target)
def restartShutdown(restart):
"""Restart (if restart = True) or shutdown (if restart = False) pep.py safely"""
msg = "We are performing some maintenance. Bancho will {} in 5 seconds. Thank you for your patience.".format("restart" if restart else "shutdown")
systemHelper.scheduleShutdown(5, restart, msg)
return msg
def systemRestart(fro, chan, message):
return restartShutdown(True)
def systemShutdown(fro, chan, message):
return restartShutdown(False)
def systemReload(fro, chan, message):
#Reload settings from bancho_settings
glob.banchoConf.loadSettings()
# Reload channels too
glob.channels.loadChannels()
# Send new channels and new bottom icon to everyone
glob.tokens.enqueueAll(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
glob.tokens.enqueueAll(serverPackets.channelInfoEnd())
for key, _ in glob.channels.channels.items():
glob.tokens.enqueueAll(serverPackets.channelInfo(key))
return "Bancho settings reloaded!"
def systemMaintenance(fro, chan, message):
# Turn on/off bancho maintenance
maintenance = True
# Get on/off
if len(message) >= 2:
if message[1] == "off":
maintenance = False
# Set new maintenance value in bancho_settings table
glob.banchoConf.setMaintenance(maintenance)
if maintenance == True:
# We have turned on maintenance mode
# Users that will be disconnected
who = []
# Disconnect everyone but mod/admins
for _, value in glob.tokens.tokens.items():
if value.rank < 3:
who.append(value.userID)
glob.tokens.enqueueAll(serverPackets.notification("Our bancho server is in maintenance mode. Please try to login again later."))
glob.tokens.multipleEnqueue(serverPackets.loginError(), who)
msg = "The server is now in maintenance mode!"
else:
# We have turned off maintenance mode
# Send message if we have turned off maintenance mode
msg = "The server is no longer in maintenance mode!"
# Chat output
return msg
def systemStatus(fro, chan, message):
# Print some server info
data = systemHelper.getSystemInfo()
# Final message
msg = "=== PEP.PY STATS ===\n"
msg += "Running pep.py server\n"
msg += "Webserver: {}\n".format(data["webServer"])
msg += "\n"
msg += "=== BANCHO STATS ===\n"
msg += "Connected users: {}\n".format(str(data["connectedUsers"]))
msg += "\n"
msg += "=== SYSTEM STATS ===\n"
msg += "CPU: {}%\n".format(str(data["cpuUsage"]))
msg += "RAM: {}GB/{}GB\n".format(str(data["usedMemory"]), str(data["totalMemory"]))
if data["unix"] == True:
msg += "Load average: {}/{}/{}\n".format(str(data["loadAverage"][0]), str(data["loadAverage"][1]), str(data["loadAverage"][2]))
return msg
"""
Commands list
trigger: message that triggers the command
callback: function to call when the command is triggered. Optional.
response: text to return when the command is triggered. Optional.
syntax: command syntax. Arguments must be separated by spaces (eg: <arg1> <arg2>)
minRank: minimum rank to execute that command. Optional (default = 1)
You MUST set trigger and callback/response, or the command won't work.
"""
commands = [
{
"trigger": "!roll",
"callback": roll
}, {
"trigger": "!faq",
"syntax": "<name>",
"callback": faq
}, {
"trigger": "!report",
"response": "Report command isn't here yet :c"
}, {
"trigger": "!help",
"response": "Click (here)[https://ripple.moe/index.php?p=16&id=4] for FokaBot's full command list"
}, {
"trigger": "!ask",
"syntax": "<question>",
"callback": ask
}, {
"trigger": "!mm00",
"response": random.choice(["meme", "MA MAURO ESISTE?"])
}, {
"trigger": "!alert",
"syntax": "<message>",
"minRank": 4,
"callback": alert
}, {
"trigger": "!moderated",
"minRank": 3,
"callback": moderated
}, {
"trigger": "!kickall",
"minRank": 4,
"callback": kickAll
}, {
"trigger": "!kick",
"syntax": "<target>",
"minRank": 3,
"callback": kick
}, {
"trigger": "!fokabot reconnect",
"minRank": 3,
"callback": fokabotReconnect
}, {
"trigger": "!silence",
"syntax": "<target> <amount> <unit(s/m/h/d)> <reason>",
"minRank": 3,
"callback": silence
}, {
"trigger": "!removesilence",
"syntax": "<target>",
"minRank": 3,
"callback": removeSilence
}, {
"trigger": "!system restart",
"minRank": 4,
"callback": systemRestart
}, {
"trigger": "!system shutdown",
"minRank": 4,
"callback": systemShutdown
}, {
"trigger": "!system reload",
"minRank": 3,
"callback": systemReload
}, {
"trigger": "!system maintenance",
"minRank": 3,
"callback": systemMaintenance
}, {
"trigger": "!system status",
"minRank": 3,
"callback": systemStatus
}, {
"trigger": "!ban",
"syntax": "<target>",
"minRank": 3,
"callback": ban
}, {
"trigger": "!unban",
"syntax": "<target>",
"minRank": 3,
"callback": unban
}
]
# Commands list default values
for cmd in commands:
cmd.setdefault("syntax", "")
cmd.setdefault("minRank", 1)
cmd.setdefault("callback", None)
cmd.setdefault("response", "u w0t m8?")

23
constants/gameModes.py Normal file
View File

@@ -0,0 +1,23 @@
"""Contains readable gamemodes with their codes"""
std = 0
taiko = 1
ctb = 2
mania = 3
def getGameModeForDB(gameMode):
"""
Convert a gamemode number to string for database table/column
gameMode -- gameMode int or variable (ex: gameMode.std)
return -- game mode readable string for db
"""
if gameMode == std:
return "std"
elif gameMode == taiko:
return "taiko"
elif gameMode == ctb:
return "ctb"
else:
return "mania"

View File

@@ -0,0 +1,2 @@
normal = 0
freeMod = 1

View File

@@ -0,0 +1,3 @@
score = 0
accuracy = 1
combo = 2

View File

@@ -0,0 +1,4 @@
headToHead = 0
tagCoop = 1
teamVs = 2
tagTeamVs = 3

3
constants/matchTeams.py Normal file
View File

@@ -0,0 +1,3 @@
noTeam = 0
blue = 1
red = 2

View File

@@ -0,0 +1,3 @@
templates = {
"!name": "You can't have that name. It goes against the [https://ripple.moe/index.php?p=23 Ripple Rules] (section \"General Rules\", \"Use an appropriate username (no offensive or *stolen* names)\"). Please tell us an appropriate (NOT STOLEN) username you'd like to have. If you ignore this message you will be banned in 3 minutes. If you fuck with us claiming repeatedly to be the person you're impersonificating without any real proof or keeping on asking to use inappropriate usernames, you'll also get banned."
}

111
constants/packetIDs.py Normal file
View File

@@ -0,0 +1,111 @@
"""Contain server and client packet IDs"""
client_changeAction = 0
client_sendPublicMessage = 1
client_logout = 2
client_requestStatusUpdate = 3
client_pong = 4
server_userID = 5
server_commandError = 6
server_sendMessage = 7
server_ping = 8
server_handleIRCUsernameChange = 9
server_handleIRCQuit = 10
server_userStats = 11
server_userLogout = 12
server_spectatorJoined = 13
server_spectatorLeft = 14
server_spectateFrames = 15
client_startSpectating = 16
client_stopSpectating = 17
client_spectateFrames = 18
server_versionUpdate = 19
client_errorReport = 20
client_cantSpectate = 21
server_spectatorCantSpectate = 22
server_getAttention = 23
server_notification = 24
client_sendPrivateMessage = 25
server_updateMatch = 26
server_newMatch = 27
server_disposeMatch = 28
client_partLobby = 29
client_joinLobby = 30
client_createMatch = 31
client_joinMatch = 32
client_partMatch = 33
#server_lobbyJoin_obsolete = 34
#server_lobbyPart_obsolete = 35
server_matchJoinSuccess = 36
server_matchJoinFail = 37
client_matchChangeSlot = 38
client_matchReady = 39
client_matchLock = 40
client_matchChangeSettings = 41
server_fellowSpectatorJoined = 42
server_fellowSpectatorLeft = 43
client_matchStart = 44
AllPlayersLoaded = 45
server_matchStart = 46
client_matchScoreUpdate = 47
server_matchScoreUpdate = 48
client_matchComplete = 49
server_matchTransferHost = 50
client_matchChangeMods = 51
client_matchLoadComplete = 52
server_matchAllPlayersLoaded = 53
client_matchNoBeatmap = 54
client_matchNotReady = 55
client_matchFailed = 56
server_matchPlayerFailed = 57
server_matchComplete = 58
client_matchHasBeatmap = 59
client_matchSkipRequest = 60
server_matchSkip = 61
server_unauthorised = 62
client_channelJoin = 63
server_channelJoinSuccess = 64
server_channelInfo = 65
server_channelKicked = 66
server_channelAvailableAutojoin = 67
client_beatmapInfoRequest = 68
server_beatmapInfoReply = 69
client_matchTransferHost = 70
server_supporterGMT = 71
server_friendsList = 72
client_friendAdd = 73
client_friendRemove = 74
server_protocolVersion = 75
server_mainMenuIcon = 76
client_matchChangeTeam = 77
client_channelPart = 78
client_receiveUpdates = 79
server_topBotnet = 80
server_matchPlayerSkipped = 81
client_setAwayMessage = 82
server_userPanel = 83
IRC_only = 84
client_userStatsRequest = 85
server_restart = 86
client_invite = 87
server_invite = 88
server_channelInfoEnd = 89
client_matchChangePassword = 90
server_matchChangePassword = 91
server_silenceEnd = 92
client_specialMatchInfoRequest = 93
server_userSilenced = 94
server_userPresenceSingle = 95
server_userPresenceBundle = 96
client_userPresenceRequest = 97
client_userPresenceRequestAll = 98
client_userToggleBlockNonFriendPM = 99
server_userPMBlocked = 100
server_targetIsSilenced = 101
server_versionUpdateForced = 102
server_switchServer = 103
server_accountRestricted = 104
server_jumpscare = 105
client_matchAbort = 106
server_switchTourneyServer = 107
client_specialJoinMatchChannel = 108
client_specialLeaveMatchChannel = 109

276
constants/serverPackets.py Normal file
View File

@@ -0,0 +1,276 @@
""" Contains functions used to write specific server packets to byte streams """
from helpers import packetHelper
from constants import dataTypes
from helpers import userHelper
from objects import glob
from constants import userRanks
from constants import packetIDs
from constants import slotStatuses
from constants import matchModModes
import random
""" Login errors packets
(userID packets derivates) """
def loginFailed():
return packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.sInt32]])
def forceUpdate():
return packetHelper.buildPacket(packetIDs.server_userID, [[-2, dataTypes.sInt32]])
def loginBanned():
packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.sInt32]])
packets += notification("You are banned. You can ask to get unbanned after 1 month since your ban by contacting support@ripple.moe")
return packets
#return packetHelper.buildPacket(packetIDs.server_userID, [[-3, dataTypes.sInt32]])
def loginError():
return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.sInt32]])
def needSupporter():
return packetHelper.buildPacket(packetIDs.server_userID, [[-6, dataTypes.sInt32]])
""" Login packets """
def userID(uid):
return packetHelper.buildPacket(packetIDs.server_userID, [[uid, dataTypes.sInt32]])
def silenceEndTime(seconds):
return packetHelper.buildPacket(packetIDs.server_silenceEnd, [[seconds, dataTypes.uInt32]])
def protocolVersion(version = 19):
return packetHelper.buildPacket(packetIDs.server_protocolVersion, [[version, dataTypes.uInt32]])
def mainMenuIcon(icon):
return packetHelper.buildPacket(packetIDs.server_mainMenuIcon, [[icon, dataTypes.string]])
def userSupporterGMT(supporter, GMT):
result = 1
if supporter == True:
result += 4
if GMT == True:
result += 2
return packetHelper.buildPacket(packetIDs.server_supporterGMT, [[result, dataTypes.uInt32]])
def friendList(userID):
friendsData = []
# Get friend IDs from db
friends = userHelper.getFriendList(userID)
# Friends number
friendsData.append([len(friends), dataTypes.uInt16])
# Add all friend user IDs to friendsData
for i in friends:
friendsData.append([i, dataTypes.sInt32])
return packetHelper.buildPacket(packetIDs.server_friendsList, friendsData)
def onlineUsers():
onlineUsersData = []
users = glob.tokens.tokens
# Users number
onlineUsersData.append([len(users), dataTypes.uInt16])
# Add all users user IDs to onlineUsersData
for _,value in users.items():
onlineUsersData.append([value.userID, dataTypes.sInt32])
return packetHelper.buildPacket(packetIDs.server_userPresenceBundle, onlineUsersData)
""" Users packets """
def userLogout(userID):
return packetHelper.buildPacket(packetIDs.server_userLogout, [[userID, dataTypes.sInt32], [0, dataTypes.byte]])
def userPanel(userID):
# Get user data
userToken = glob.tokens.getTokenFromUserID(userID)
username = userHelper.getUsername(userID)
timezone = 24 # TODO: Timezone
country = userToken.getCountry()
gameRank = userHelper.getGameRank(userID, userToken.gameMode)
latitude = userToken.getLatitude()
longitude = userToken.getLongitude()
# Get username color according to rank
# Only admins and normal users are currently supported
rank = userHelper.getRankPrivileges(userID)
if username == "FokaBot":
userRank = userRanks.MOD
elif rank == 4:
userRank = userRanks.ADMIN
elif rank == 3:
userRank = userRanks.MOD
elif rank == 2:
userRank = userRanks.SUPPORTER
else:
userRank = userRanks.NORMAL
return packetHelper.buildPacket(packetIDs.server_userPanel,
[
[userID, dataTypes.sInt32],
[username, dataTypes.string],
[timezone, dataTypes.byte],
[country, dataTypes.byte],
[userRank, dataTypes.byte],
[longitude, dataTypes.ffloat],
[latitude, dataTypes.ffloat],
[gameRank, dataTypes.uInt32]
])
def userStats(userID):
# Get userID's token from tokens list
userToken = glob.tokens.getTokenFromUserID(userID)
# Get stats from DB
# TODO: Caching system
rankedScore = userHelper.getRankedScore(userID, userToken.gameMode)
accuracy = userHelper.getAccuracy(userID, userToken.gameMode)/100
playcount = userHelper.getPlaycount(userID, userToken.gameMode)
totalScore = userHelper.getTotalScore(userID, userToken.gameMode)
gameRank = userHelper.getGameRank(userID, userToken.gameMode)
pp = int(userHelper.getPP(userID, userToken.gameMode))
return packetHelper.buildPacket(packetIDs.server_userStats,
[
[userID, dataTypes.uInt32],
[userToken.actionID, dataTypes.byte],
[userToken.actionText, dataTypes.string],
[userToken.actionMd5, dataTypes.string],
[userToken.actionMods, dataTypes.sInt32],
[userToken.gameMode, dataTypes.byte],
[0, dataTypes.sInt32],
[rankedScore, dataTypes.uInt64],
[accuracy, dataTypes.ffloat],
[playcount, dataTypes.uInt32],
[totalScore, dataTypes.uInt64],
[gameRank, dataTypes.uInt32],
[pp, dataTypes.uInt16]
])
""" Chat packets """
def sendMessage(fro, to, message):
return packetHelper.buildPacket(packetIDs.server_sendMessage, [[fro, dataTypes.string], [message, dataTypes.string], [to, dataTypes.string], [userHelper.getID(fro), dataTypes.sInt32]])
def channelJoinSuccess(userID, chan):
return packetHelper.buildPacket(packetIDs.server_channelJoinSuccess, [[chan, dataTypes.string]])
def channelInfo(chan):
channel = glob.channels.channels[chan]
return packetHelper.buildPacket(packetIDs.server_channelInfo, [[chan, dataTypes.string], [channel.description, dataTypes.string], [channel.getConnectedUsersCount(), dataTypes.uInt16]])
def channelInfoEnd():
return packetHelper.buildPacket(packetIDs.server_channelInfoEnd, [[0, dataTypes.uInt32]])
def channelKicked(chan):
return packetHelper.buildPacket(packetIDs.server_channelKicked, [[chan, dataTypes.string]])
""" Spectator packets """
def addSpectator(userID):
return packetHelper.buildPacket(packetIDs.server_spectatorJoined, [[userID, dataTypes.sInt32]])
def removeSpectator(userID):
return packetHelper.buildPacket(packetIDs.server_spectatorLeft, [[userID, dataTypes.sInt32]])
def spectatorFrames(data):
return packetHelper.buildPacket(packetIDs.server_spectateFrames, [[data, dataTypes.bbytes]])
def noSongSpectator(userID):
return packetHelper.buildPacket(packetIDs.server_spectatorCantSpectate, [[userID, dataTypes.sInt32]])
""" Multiplayer Packets """
def createMatch(matchID):
# Make sure the match exists
if matchID not in glob.matches.matches:
return None
# Get match binary data and build packet
match = glob.matches.matches[matchID]
return packetHelper.buildPacket(packetIDs.server_newMatch, match.getMatchData())
def updateMatch(matchID):
# Make sure the match exists
if matchID not in glob.matches.matches:
return None
# Get match binary data and build packet
match = glob.matches.matches[matchID]
return packetHelper.buildPacket(packetIDs.server_updateMatch, match.getMatchData())
def matchStart(matchID):
# Make sure the match exists
if matchID not in glob.matches.matches:
return None
# Get match binary data and build packet
match = glob.matches.matches[matchID]
return packetHelper.buildPacket(packetIDs.server_matchStart, match.getMatchData())
def disposeMatch(matchID):
return packetHelper.buildPacket(packetIDs.server_disposeMatch, [[matchID, dataTypes.uInt16]])
def matchJoinSuccess(matchID):
# Make sure the match exists
if matchID not in glob.matches.matches:
return None
# Get match binary data and build packet
match = glob.matches.matches[matchID]
data = packetHelper.buildPacket(packetIDs.server_matchJoinSuccess, match.getMatchData())
return data
def matchJoinFail():
return packetHelper.buildPacket(packetIDs.server_matchJoinFail)
def changeMatchPassword(newPassword):
return packetHelper.buildPacket(packetIDs.server_matchChangePassword, [[newPassword, dataTypes.string]])
def allPlayersLoaded():
return packetHelper.buildPacket(packetIDs.server_matchAllPlayersLoaded)
def playerSkipped(userID):
return packetHelper.buildPacket(packetIDs.server_matchPlayerSkipped, [[userID, dataTypes.sInt32]])
def allPlayersSkipped():
return packetHelper.buildPacket(packetIDs.server_matchSkip)
def matchFrames(slotID, data):
return packetHelper.buildPacket(packetIDs.server_matchScoreUpdate, [[data[7:11], dataTypes.bbytes], [slotID, dataTypes.byte], [data[12:], dataTypes.bbytes]])
def matchComplete():
return packetHelper.buildPacket(packetIDs.server_matchComplete)
def playerFailed(slotID):
return packetHelper.buildPacket(packetIDs.server_matchPlayerFailed, [[slotID, dataTypes.uInt32]])
def matchTransferHost():
return packetHelper.buildPacket(packetIDs.server_matchTransferHost)
""" Other packets """
def notification(message):
return packetHelper.buildPacket(packetIDs.server_notification, [[message, dataTypes.string]])
def jumpscare(message):
return packetHelper.buildPacket(packetIDs.server_jumpscare, [[message, dataTypes.string]])
def banchoRestart(msUntilReconnection):
return packetHelper.buildPacket(packetIDs.server_restart, [[msUntilReconnection, dataTypes.uInt32]])
""" WIP Packets """
def getAttention():
return packetHelper.buildPacket(packetIDs.server_getAttention)
def packet80():
return packetHelper.buildPacket(packetIDs.server_topBotnet)

View File

@@ -0,0 +1,8 @@
free = 1
locked = 2
notReady = 4
ready = 8
noMap = 16
playing = 32
occupied = 124
playingQuit = 128

9
constants/userRanks.py Normal file
View File

@@ -0,0 +1,9 @@
"""Bancho user ranks"""
# TODO: Uppercase, maybe?
NORMAL = 0
PLAYER = 1
SUPPORTER = 4
MOD = 6
PEPPY = 8
ADMIN = 16
TOURNAMENTSTAFF = 32