.BANCHO. pep.py modules
This commit is contained in:
143
constants/clientPackets.py
Normal file
143
constants/clientPackets.py
Normal 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
12
constants/dataTypes.py
Normal 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
58
constants/exceptions.py
Normal 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
|
402
constants/fokabotCommands.py
Normal file
402
constants/fokabotCommands.py
Normal 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
23
constants/gameModes.py
Normal 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"
|
2
constants/matchModModes.py
Normal file
2
constants/matchModModes.py
Normal file
@@ -0,0 +1,2 @@
|
||||
normal = 0
|
||||
freeMod = 1
|
3
constants/matchScoringTypes.py
Normal file
3
constants/matchScoringTypes.py
Normal file
@@ -0,0 +1,3 @@
|
||||
score = 0
|
||||
accuracy = 1
|
||||
combo = 2
|
4
constants/matchTeamTypes.py
Normal file
4
constants/matchTeamTypes.py
Normal file
@@ -0,0 +1,4 @@
|
||||
headToHead = 0
|
||||
tagCoop = 1
|
||||
teamVs = 2
|
||||
tagTeamVs = 3
|
3
constants/matchTeams.py
Normal file
3
constants/matchTeams.py
Normal file
@@ -0,0 +1,3 @@
|
||||
noTeam = 0
|
||||
blue = 1
|
||||
red = 2
|
3
constants/messageTemplates.py
Normal file
3
constants/messageTemplates.py
Normal 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
111
constants/packetIDs.py
Normal 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
276
constants/serverPackets.py
Normal 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)
|
8
constants/slotStatuses.py
Normal file
8
constants/slotStatuses.py
Normal 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
9
constants/userRanks.py
Normal 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
|
Reference in New Issue
Block a user