IRC Support for username with spaces

BATs with Donor have bright yellow username in chat
General performance improvements
Code cleaning
Multiplayer improvements and fixes
Fixed some spectator bugs
This commit is contained in:
Nyo 2016-09-02 12:41:19 +02:00
parent e16e4d7493
commit 653303831b
47 changed files with 450 additions and 622 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
config.ini
filters.txt
.data
.idea

View File

@ -57,13 +57,12 @@ def addRemoveFriend(stream):
return packetHelper.readPacketData(stream, [["friendID", dataTypes.sInt32]])
""" SPECTATOR PACKETS """
""" Spectator packets """
def startSpectating(stream):
return packetHelper.readPacketData(stream,[["userID", dataTypes.sInt32]])
""" MULTIPLAYER PACKETS """
""" Multiplayer packets """
def matchSettings(stream):
# Data to return, will be merged later
data = []
@ -115,9 +114,6 @@ def matchSettings(stream):
# 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)

View File

@ -25,8 +25,8 @@ message -- list containing arguments passed from the message
. . .
return the message or **False** if there's no response by the bot
TODO: Change False to None, because False doesn't make any sense
"""
def instantRestart(fro, chan, message):
glob.tokens.enqueueAll(serverPackets.notification("We are restarting Bancho. Be right back!"))
systemHelper.scheduleShutdown(0, True, delay=1)

View File

@ -1,4 +1,3 @@
"""Contains readable gamemodes with their codes"""
std = 0
taiko = 1
ctb = 2
@ -9,10 +8,8 @@ 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:
@ -27,10 +24,8 @@ def getGameModeForPrinting(gameMode):
Convert a gamemode number to string for showing to a user (e.g. !last)
gameMode -- gameMode int or variable (ex: gameMode.std)
return -- game mode readable string for a human
"""
if gameMode == std:
return "osu!"
elif gameMode == taiko:

View File

@ -5,9 +5,9 @@ from helpers import userHelper
from objects import glob
from constants import userRanks
from constants import packetIDs
from constants import privileges
""" Login errors packets
(userID packets derivates) """
""" Login errors packets """
def loginFailed():
return packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.sInt32]])
@ -18,7 +18,6 @@ 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]])
@ -29,6 +28,7 @@ def needSupporter():
def needVerification():
return packetHelper.buildPacket(packetIDs.server_userID, [[-8, dataTypes.sInt32]])
""" Login packets """
def userID(uid):
return packetHelper.buildPacket(packetIDs.server_userID, [[uid, dataTypes.sInt32]])
@ -51,19 +51,8 @@ def userSupporterGMT(supporter, GMT):
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)
return packetHelper.buildPacket(packetIDs.server_friendsList, [[friends, dataTypes.intList]])
def onlineUsers():
userIDs = []
@ -91,7 +80,7 @@ def userPanel(userID, force = False):
# Get user data
username = userToken.username
timezone = 24+userToken.timeOffset # TODO: Timezone
timezone = 24+userToken.timeOffset
country = userToken.country
gameRank = userToken.gameRank
latitude = userToken.getLatitude()
@ -105,7 +94,7 @@ def userPanel(userID, force = False):
userRank = userRanks.MOD
elif userHelper.isInPrivilegeGroup(userID, "developer") == True:
userRank = userRanks.ADMIN
elif userHelper.isInPrivilegeGroup(userID, "donor") == True:
elif (userToken.privileges & privileges.USER_DONOR) > 0:
userRank = userRanks.SUPPORTER
else:
userRank = userRanks.NORMAL
@ -126,10 +115,12 @@ def userPanel(userID, force = False):
def userStats(userID, force = False):
# Get userID's token from tokens list
userToken = glob.tokens.getTokenFromUserID(userID)
if userToken == None:
return bytes()
if (userToken.restricted == True or userToken.irc == True) and force == False:
return bytes()
return packetHelper.buildPacket(packetIDs.server_userStats,
[
[userID, dataTypes.uInt32],
@ -150,14 +141,23 @@ def userStats(userID, force = False):
""" 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]])
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]])
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]])
@ -199,7 +199,6 @@ def createMatch(matchID):
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:
@ -209,7 +208,6 @@ def updateMatch(matchID):
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:
@ -219,9 +217,8 @@ def matchStart(matchID):
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]])
return packetHelper.buildPacket(packetIDs.server_disposeMatch, [[matchID, dataTypes.uInt32]])
def matchJoinSuccess(matchID):
# Make sure the match exists
@ -260,9 +257,10 @@ def playerFailed(slotID):
def matchTransferHost():
return packetHelper.buildPacket(packetIDs.server_matchTransferHost)
""" Other packets """
def notification(message):
return packetHelper.buildPacket(packetIDs.server_notification, [[message, dataTypes.string]])
def banchoRestart(msUntilReconnection):
return packetHelper.buildPacket(packetIDs.server_restart, [[msUntilReconnection, dataTypes.uInt32]])
return packetHelper.buildPacket(packetIDs.server_restart, [[msUntilReconnection, dataTypes.uInt32]])

View File

@ -4,7 +4,6 @@ from constants import serverPackets
from helpers import userHelper
from helpers import logHelper as log
from constants import actions
from helpers import chatHelper as chat
def handle(userToken, packetData):
# Get usertoken data
@ -24,9 +23,16 @@ def handle(userToken, packetData):
# Change action packet
packetData = clientPackets.userActionChange(packetData)
# If we are not in spectate status but we're spectating someone, stop spectating
#if userToken.spectating != 0 and userToken.actionID != actions.watching and userToken.actionID != actions.idle and userToken.actionID != actions.afk:
# userToken.stopSpectating()
# If we are not in multiplayer but we are in a match, part match
#if userToken.matchID != -1 and userToken.actionID != actions.multiplaying and userToken.actionID != actions.multiplayer and userToken.actionID != actions.afk:
# userToken.partMatch()
# Update cached stats if our pp changedm if we've just submitted a score or we've changed gameMode
if (userToken.actionID == actions.playing or userToken.actionID == actions.multiplaying) or (userToken.pp != userHelper.getPP(userID, userToken.gameMode)) or (userToken.gameMode != packetData["gameMode"]):
log.debug("!!!! UPDATING CACHED STATS !!!!")
# Always update game mode, or we'll cache stats from the wrong game mode if we've changed it
userToken.gameMode = packetData["gameMode"]
userToken.updateCachedStats()
@ -55,12 +61,5 @@ def handle(userToken, packetData):
token.enqueue(serverPackets.userPanel(userID, force))
token.enqueue(serverPackets.userStats(userID, force))
# Send osu!direct alert if needed
# NOTE: Remove this when osu!direct will be fixed
if userToken.actionID == actions.osuDirect and userToken.osuDirectAlert == False:
userToken.osuDirectAlert = True
chat.sendMessage("FokaBot", userToken.username, "Sup! osu!direct works, but you'll need to update the switcher to have the Download button working. If you didn't update the switcher yet, please do!")
# Console output
log.info("{} changed action: {} [{}][{}]".format(username, str(userToken.actionID), userToken.actionText, userToken.actionMd5))

View File

@ -16,6 +16,10 @@ def handle(userToken, packetData):
return
match = glob.matches.matches[matchID]
# Host check
if userID != match.hostUserID:
return
# Set slot or match mods according to modType
if match.matchModMode == matchModModes.freeMod:
# Freemod
@ -25,7 +29,7 @@ def handle(userToken, packetData):
# If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod
if (packetData["mods"] & mods.DoubleTime) > 0:
match.changeMatchMods(mods.DoubleTime)
# Nighcore
# Nightcore
if (packetData["mods"] & mods.Nightcore) > 0:
match.changeMatchMods(match.mods+mods.Nightcore)
elif (packetData["mods"] & mods.HalfTime) > 0:

View File

@ -13,5 +13,9 @@ def handle(userToken, packetData):
# Get our match
match = glob.matches.matches[matchID]
# Host check
if userToken.userID != match.hostUserID:
return
# Update match password
match.changePassword(packetData["matchPassword"])

View File

@ -6,6 +6,7 @@ from constants import matchTeamTypes
from constants import matchTeams
from constants import slotStatuses
from helpers import logHelper as log
from helpers import generalFunctions
def handle(userToken, packetData):
# Read new settings
@ -13,7 +14,7 @@ def handle(userToken, packetData):
# Get match ID
matchID = userToken.matchID
# Make sure the match exists
if matchID not in glob.matches.matches:
return
@ -21,6 +22,10 @@ def handle(userToken, packetData):
# Get match object
match = glob.matches.matches[matchID]
# Host check
if userToken.userID != match.hostUserID:
return
# Some dank memes easter egg
memeTitles = [
"RWC 2020",
@ -53,7 +58,10 @@ def handle(userToken, packetData):
# Update match settings
match.inProgress = packetData["inProgress"]
match.matchPassword = packetData["matchPassword"]
if packetData["matchPassword"] != "":
match.matchPassword = generalFunctions.stringMd5(packetData["matchPassword"])
else:
match.matchPassword = ""
match.beatmapName = packetData["beatmapName"]
match.beatmapID = packetData["beatmapID"]
match.hostUserID = packetData["hostUserID"]
@ -71,14 +79,14 @@ def handle(userToken, packetData):
# Reset ready if needed
if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5:
for i in range(0,16):
if match.slots[i]["status"] == slotStatuses.ready:
match.slots[i]["status"] = slotStatuses.notReady
if match.slots[i].status == slotStatuses.ready:
match.slots[i].status = slotStatuses.notReady
# Reset mods if needed
if match.matchModMode == matchModModes.normal:
# Reset slot mods if not freeMods
for i in range(0,16):
match.slots[i]["mods"] = 0
match.slots[i].mods = 0
else:
# Reset match mods if freemod
match.mods = 0
@ -88,13 +96,13 @@ def handle(userToken, packetData):
# Set teams
c=0
for i in range(0,16):
if match.slots[i]["team"] == matchTeams.noTeam:
match.slots[i]["team"] = matchTeams.red if c % 2 == 0 else matchTeams.blue
if match.slots[i].team == matchTeams.noTeam:
match.slots[i].team = matchTeams.red if c % 2 == 0 else matchTeams.blue
c+=1
else:
# Reset teams
for i in range(0,16):
match.slots[i]["team"] = matchTeams.noTeam
match.slots[i].team = matchTeams.noTeam
# Force no freemods if tag coop
if match.matchTeamType == matchTeamTypes.tagCoop or match.matchTeamType == matchTeamTypes.tagTeamVs:
@ -105,4 +113,3 @@ def handle(userToken, packetData):
# Console output
log.info("MPROOM{}: Updated room settings".format(match.matchID))
#consoleHelper.printColored("> MPROOM{}: DEBUG: Host is {}".format(match.matchID, match.hostUserID), bcolors.PINK)

View File

@ -4,6 +4,7 @@ from objects import glob
from constants import exceptions
from helpers import logHelper as log
from helpers import chatHelper as chat
from helpers import generalFunctions
def handle(userToken, packetData):
# read packet data
@ -12,12 +13,15 @@ def handle(userToken, packetData):
# Get match from ID
joinMatch(userToken, packetData["matchID"], packetData["password"])
def joinMatch(userToken, matchID, password):
def joinMatch(userToken, matchID, password, isPasswordHashed = False):
try:
# TODO: leave other matches
# TODO: Stop spectating
# Stop spectating
userToken.stopSpectating()
# get usertoken data
# Leave other matches
userToken.partMatch()
# Get usertoken data
userID = userToken.userID
# Make sure the match exists
@ -27,6 +31,10 @@ def joinMatch(userToken, matchID, password):
# Match exists, get object
match = glob.matches.matches[matchID]
# Hash password if needed
if isPasswordHashed == False and password != "":
password = generalFunctions.stringMd5(password)
# Check password
# TODO: Admins can enter every match
if match.matchPassword != "":

View File

@ -2,16 +2,10 @@ from helpers import userHelper
from constants import serverPackets
from constants import exceptions
from objects import glob
from helpers import consoleHelper
from constants import bcolors
from helpers import locationHelper
from helpers import countryHelper
import time
from helpers import generalFunctions
import sys
import traceback
from helpers import requestHelper
from helpers import discordBotHelper
from helpers import logHelper as log
from helpers import chatHelper as chat
from constants import privileges

View File

@ -15,21 +15,16 @@ def handle(userToken, _=None):
# the server, so we accept logout packets sent at least 5 seconds after login
# if the user logs out before 5 seconds, he will be disconnected later with timeout check
if int(time.time()-userToken.loginTime) >= 5 or userToken.irc == True:
# Stop spectating if needed
# TODO: Call stopSpectatingEvent!!!!!!!!!
if userToken.spectating != 0:
# The user was spectating someone
spectatorHostToken = glob.tokens.getTokenFromUserID(userToken.spectating)
if spectatorHostToken != None:
# The host is still online, send removeSpectator to him
spectatorHostToken.enqueue(serverPackets.removeSpectator(userID))
# Stop spectating
userToken.stopSpectating()
# Part matches
userToken.partMatch()
# Part all joined channels
for i in userToken.joinedChannels:
chat.partChannel(token=userToken, channel=i)
# TODO: Lobby left if joined
# Enqueue our disconnection to everyone else
glob.tokens.enqueueAll(serverPackets.userLogout(userID))

View File

@ -25,7 +25,7 @@ def handle(userToken, packetData):
# Enqueue frames to who's playing
for i in range(0,16):
if match.slots[i]["userID"] > -1 and match.slots[i]["status"] == slotStatuses.playing:
token = glob.tokens.getTokenFromUserID(match.slots[i]["userID"])
if match.slots[i].userID > -1 and match.slots[i].status == slotStatuses.playing:
token = glob.tokens.getTokenFromUserID(match.slots[i].userID)
if token != None:
token.enqueue(serverPackets.matchFrames(slotID, packetData))

View File

@ -1,3 +1,4 @@
from events import matchBeatmapEvent
def handle(userToken, packetData):
matchBeatmapEvent.handle(userToken, packetData, True)

View File

@ -14,6 +14,10 @@ def handle(userToken, packetData):
return
match = glob.matches.matches[matchID]
# Host check
if userID != match.hostUserID:
return
# Make sure we aren't locking our slot
ourSlot = match.getUserSlotID(userID)
if packetData["slotID"] == ourSlot:

View File

@ -1,3 +1,4 @@
from events import matchBeatmapEvent
def handle(userToken, packetData):
matchBeatmapEvent.handle(userToken, packetData, False)

View File

@ -3,7 +3,6 @@ from constants import slotStatuses
from constants import serverPackets
def handle(userToken, _):
# TODO: Host check
# Get match ID and match object
matchID = userToken.matchID
@ -19,10 +18,12 @@ def handle(userToken, _):
# The match exists, get object
match = glob.matches.matches[matchID]
force = False # TODO: Force thing
# Host check
if userToken.userID != match.hostUserID:
return
# Make sure we have enough players
if (match.countUsers() < 2 or not match.checkTeams()) and not force:
if (match.countUsers() < 2 or match.checkTeams() == False):
return
# Change inProgress value
@ -30,16 +31,16 @@ def handle(userToken, _):
# Set playing to ready players and set load, skip and complete to False
for i in range(0,16):
if (match.slots[i]["status"] & slotStatuses.ready) > 0:
match.slots[i]["status"] = slotStatuses.playing
match.slots[i]["loaded"] = False
match.slots[i]["skip"] = False
match.slots[i]["complete"] = False
if (match.slots[i].status & slotStatuses.ready) > 0:
match.slots[i].status = slotStatuses.playing
match.slots[i].loaded = False
match.slots[i].skip = False
match.slots[i].complete = False
# Send match start packet
for i in range(0,16):
if (match.slots[i]["status"] & slotStatuses.playing) > 0 and match.slots[i]["userID"] != -1:
token = glob.tokens.getTokenFromUserID(match.slots[i]["userID"])
if (match.slots[i].status & slotStatuses.playing) > 0 and match.slots[i].userID != -1:
token = glob.tokens.getTokenFromUserID(match.slots[i].userID)
if token != None:
token.enqueue(serverPackets.matchStart(matchID))

View File

@ -19,5 +19,9 @@ def handle(userToken, packetData):
# Match exists, get object
match = glob.matches.matches[matchID]
# Host check
if userToken.userID != match.hostUserID:
return
# Transfer host
match.transferHost(packetData["slotID"])

View File

@ -1,28 +1,2 @@
from objects import glob
def handle(userToken, _):
# get data from usertoken
userID = userToken.userID
# Get match ID and match object
matchID = userToken.matchID
# Make sure we are in a match
if matchID == -1:
return
# Make sure the match exists
if matchID not in glob.matches.matches:
return
# The match exists, get object
match = glob.matches.matches[matchID]
# Set slot to free
match.userLeft(userID)
# Part #multiplayer channel
#chat.partChannel(token=userToken, channel="#multi_{}".format(matchID), kick=True)
# Set usertoken match to -1
userToken.partMatch()
def handle(userToken, _=None):
userToken.partMatch()

View File

@ -2,8 +2,6 @@ from constants import serverPackets
from helpers import logHelper as log
def handle(userToken, packetData):
log.debug("Requested status update")
# Update cache and send new stats
userToken.updateCachedStats()
userToken.enqueue(serverPackets.userStats(userToken.userID))

View File

@ -1,37 +1,2 @@
from objects import glob
from constants import serverPackets
from constants import exceptions
from helpers import logHelper as log
from helpers import chatHelper as chat
def handle(userToken, _):
try:
# get user token data
userID = userToken.userID
username = userToken.username
# Remove our userID from host's spectators
target = userToken.spectating
targetToken = glob.tokens.getTokenFromUserID(target)
if targetToken == None:
raise exceptions.tokenNotFoundException
targetToken.removeSpectator(userID)
# Part #spectator channel
chat.partChannel(token=userToken, channel="#spect_{}".format(target))
# Send the spectator left packet to host
targetToken.enqueue(serverPackets.removeSpectator(userID))
for c in targetToken.spectators:
spec = glob.tokens.getTokenFromUserID(c)
spec.enqueue(serverPackets.fellowSpectatorLeft(userID))
#targetToken.enqueue(serverPackets.fellowSpectatorLeft(userID))
# Console output
log.info("{} are no longer spectating {}".format(username, target))
except exceptions.tokenNotFoundException:
log.warning("Spectator stop: token not found")
finally:
# Set our spectating user to 0
userToken.stopSpectating()
def handle(userToken, _=None):
userToken.stopSpectating()

View File

@ -33,7 +33,5 @@ class handler(requestHelper.asyncRequestHandler):
data["status"] = statusCode
# Send response
#self.clear()
self.write(json.dumps(data))
self.set_status(statusCode)
#self.finish(json.dumps(data))

View File

@ -226,8 +226,8 @@ class handler(SentryMixin, requestHelper.asyncRequestHandler):
self.set_status(200)
self.add_header("cho-token", responseTokenString)
self.add_header("cho-protocol", "19")
#self.add_header("Keep-Alive", "timeout=5, max=100")
#self.add_header("Connection", "keep-alive")
self.add_header("Connection", "keep-alive")
self.add_header("Keep-Alive", "timeout=5, max=100")
self.add_header("Content-Type", "text/html; charset=UTF-8")
except:
log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
@ -259,6 +259,4 @@ class handler(SentryMixin, requestHelper.asyncRequestHandler):
html += " \\ . .. .. . /<br>"
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br><i>&copy; Ripple team, 2016</i></pre></body></html>"
self.write(html)
#yield tornado.gen.Task(self.captureMessage, "test")
#self.finish()
self.write(html)

View File

@ -1,52 +0,0 @@
"""
WIP feature that will come in the future.
Don't import
"""
import flask
from objects import glob
from constants import exceptions
@app.route("/api/online-users-count")
def APIonlineUsersCount():
return flask.jsonify({"count" : len(glob.tokens.tokens)-1})
@app.route("/api/user-info")
def APIonlineUsers():
resp = {}
try:
u = flask.request.args.get('u')
# Username/userID
if u.isdigit():
u = int(u)
else:
u = userHelper.getID(u)
if u == None:
raise exceptions.userNotFoundException
# Make sure this user is online
userToken = glob.tokens.getTokenFromUserID(u)
if userToken == None:
raise exceptions.tokenNotFoundException
# Build response dictionary
resp["response"] = "1"
resp[userToken.username] = {
"userID" : userToken.userID,
"actionID" : userToken.actionID,
"actionText" : userToken.actionText,
"actionMd5" : userToken.actionMd5,
"actionMods": userToken.actionMods,
"gameMode": userToken.gameMode,
"country": countryHelper.getCountryLetters(userToken.country),
"position": userToken.location,
"spectating": userToken.spectating,
"spectators": userToken.spectators
}
except exceptions.userNotFoundException:
resp["response"] = "-1"
except exceptions.tokenNotFoundException:
resp["response"] = "-2"
finally:
return flask.jsonify(resp)

View File

@ -147,9 +147,6 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
log.warning("User not connected to IRC/Bancho")
return 442 # idk
def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
"""
Send a message to osu!bancho and IRC server
@ -299,8 +296,13 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
return 401
""" IRC-Bancho Connect/Disconnect/Join/Part interfaces"""
def fixUsernameForBancho(username):
return username.replace("_", " ")
def fixUsernameForIRC(username):
return username.replace(" ", "_")
def IRCConnect(username):
userID = userHelper.getID(username)
if userID == False:

View File

@ -1,18 +1,7 @@
import os
import configparser
class config:
"""
config.ini object
config -- list with ini data
default -- if true, we have generated a default config.ini
"""
config = configparser.ConfigParser()
fileName = "" # config filename
default = True
class config():
# Check if config.ini exists and load/generate it
def __init__(self, file):
"""
@ -20,7 +9,8 @@ class config:
file -- filename
"""
self.config = configparser.ConfigParser()
self.default = True
self.fileName = file
if os.path.isfile(self.fileName):
# config.ini found, load it
@ -39,7 +29,6 @@ class config:
return -- True if valid, False if not
"""
try:
# Try to get all the required keys
self.config.get("db","host")
@ -75,11 +64,10 @@ class config:
except:
return False
# Generate a default config.ini
def generateDefaultConfig(self):
"""Open and set default keys for that config file"""
"""
Open and set default keys for that config file
"""
# Open config.ini in write mode
f = open(self.fileName, "w")

View File

@ -1,13 +1,12 @@
"""Some console related functions"""
from constants import bcolors
from objects import glob
def printServerStartHeader(asciiArt):
"""Print server start header with optional ascii art
asciiArt -- if True, will print ascii art too"""
"""
Print server start header with optional ascii art
asciiArt -- if True, will print ascii art too
"""
if asciiArt == True:
print("{} _ __".format(bcolors.GREEN))
print(" (_) / /")
@ -28,20 +27,17 @@ def printServerStartHeader(asciiArt):
printColored("> Welcome to pep.py osu!bancho server v{}".format(glob.VERSION), bcolors.GREEN)
printColored("> Made by the Ripple team", bcolors.GREEN)
printColored("> {}https://github.com/osuripple/ripple".format(bcolors.UNDERLINE), bcolors.GREEN)
printColored("> {}https://git.zxq.co/ripple/pep.py".format(bcolors.UNDERLINE), bcolors.GREEN)
printColored("> Press CTRL+C to exit\n",bcolors.GREEN)
def printNoNl(string):
"""
Print string without new line at the end
string -- string to print
"""
print(string, end="")
def printColored(string, color):
"""
Print colored string
@ -49,23 +45,22 @@ def printColored(string, color):
string -- string to print
color -- see bcolors.py
"""
print("{}{}{}".format(color, string, bcolors.ENDC))
def printError():
"""Print error text FOR LOADING"""
"""
Print error text FOR LOADING
"""
printColored("Error", bcolors.RED)
def printDone():
"""Print error text FOR LOADING"""
"""
Print error text FOR LOADING
"""
printColored("Done", bcolors.GREEN)
def printWarning():
"""Print error text FOR LOADING"""
"""
Print error text FOR LOADING
"""
printColored("Warning", bcolors.YELLOW)

View File

@ -253,7 +253,6 @@ countryCodes = {
"AI": 7
}
def getCountryID(code):
"""
Get country ID for osu client

View File

@ -2,13 +2,13 @@ import MySQLdb
import threading
from helpers import logHelper as log
class mysqlWorker:
class mysqlWorker():
"""
Instance of a pettirosso meme
Instance of a mysql worker
"""
def __init__(self, wid, host, username, password, database):
"""
Create a pettirosso meme (mysql worker)
Create a mysql worker
wid -- worker id
host -- hostname
@ -22,11 +22,10 @@ class mysqlWorker:
self.ready = True
self.lock = threading.Lock()
class db:
class db():
"""
A MySQL db connection with multiple workers
"""
def __init__(self, host, username, password, database, workers):
"""
Create MySQL workers aka pettirossi meme
@ -37,9 +36,6 @@ class db:
database -- MySQL database name
workers -- Number of workers to spawn
"""
#self.lock = threading.Lock()
#self.connection = MySQLdb.connect(host, username, password, database)
self.workers = []
self.lastWorker = 0
self.workersNumber = workers
@ -57,7 +53,6 @@ class db:
self.lastWorker = 0
else:
self.lastWorker += 1
#print("Using worker {}".format(self.lastWorker))
return self.workers[self.lastWorker]
def execute(self, query, params = ()):

View File

@ -1,9 +1,6 @@
import requests
from objects import glob
from helpers import generalFunctions
from urllib.parse import urlencode
from helpers import consoleHelper
from constants import bcolors
def sendDiscordMessage(channel, message, alertDev = False, prefix = "**pep.py**"):
"""
@ -24,7 +21,6 @@ def sendDiscordMessage(channel, message, alertDev = False, prefix = "**pep.py**"
except:
continue
def sendConfidential(message, alertDev = False):
"""
Send a message to #bunker
@ -33,7 +29,6 @@ def sendConfidential(message, alertDev = False):
"""
sendDiscordMessage("bunk", message, alertDev)
def sendStaff(message):
"""
Send a message to #staff
@ -42,7 +37,6 @@ def sendStaff(message):
"""
sendDiscordMessage("staff", message)
def sendGeneral(message):
"""
Send a message to #general
@ -51,7 +45,6 @@ def sendGeneral(message):
"""
sendDiscordMessage("general", message)
def sendChatlog(message):
"""
Send a message to #chatlog

View File

@ -1,6 +1,17 @@
"""Some functions that don't fit in any other file"""
from constants import mods
from time import gmtime, strftime
import hashlib
def stringMd5(string):
"""
Return string's md5
string -- string to hash
return -- string's md5 hash
"""
d = hashlib.md5()
d.update(string.encode("utf-8"))
return d.hexdigest()
def stringToBool(s):
"""
@ -9,10 +20,8 @@ def stringToBool(s):
s -- string/int value
return -- True/False
"""
return (s == "True" or s== "true" or s == "1" or s == 1)
def hexString(s):
"""
Output s' bytes in HEX
@ -20,7 +29,6 @@ def hexString(s):
s -- string
return -- string with hex value
"""
return ":".join("{:02x}".format(ord(str(c))) for c in s)
def readableMods(__mods):

View File

@ -11,7 +11,6 @@ def getCountry(ip):
ip -- IP Address
return -- Country code (2 letters)
"""
try:
# Try to get country from Pikolo Aul's Go-Sanic ip API
result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["country"]
@ -20,7 +19,6 @@ def getCountry(ip):
log.error("Error in get country")
return "XX"
def getLocation(ip):
"""
Get latitude and longitude from IP address
@ -28,7 +26,6 @@ def getLocation(ip):
ip -- IP address
return -- [latitude, longitude]
"""
try:
# Try to get position from Pikolo Aul's Go-Sanic ip API
result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["loc"].split(",")

View File

@ -8,7 +8,6 @@ def uleb128Encode(num):
num -- int to encode
return -- bytearray with encoded number
"""
arr = bytearray()
length = 0
@ -24,7 +23,6 @@ def uleb128Encode(num):
return arr
def uleb128Decode(num):
"""
Decode uleb128 -> int
@ -32,9 +30,7 @@ def uleb128Decode(num):
num -- encoded uleb128
return -- list. [total, length]
"""
shift = 0
arr = [0,0] #total, length
while True:
@ -47,42 +43,40 @@ def uleb128Decode(num):
return arr
def unpackData(__data, __dataType):
def unpackData(data, dataType):
"""
Unpacks data according to dataType
__data -- bytes array to unpack
__dataType -- data type. See dataTypes.py
data -- bytes array to unpack
dataType -- data type. See dataTypes.py
return -- unpacked bytes
"""
# Get right pack Type
if __dataType == dataTypes.uInt16:
if dataType == dataTypes.uInt16:
unpackType = "<H"
elif __dataType == dataTypes.sInt16:
elif dataType == dataTypes.sInt16:
unpackType = "<h"
elif __dataType == dataTypes.uInt32:
elif dataType == dataTypes.uInt32:
unpackType = "<L"
elif __dataType == dataTypes.sInt32:
elif dataType == dataTypes.sInt32:
unpackType = "<l"
elif __dataType == dataTypes.uInt64:
elif dataType == dataTypes.uInt64:
unpackType = "<Q"
elif __dataType == dataTypes.sInt64:
elif dataType == dataTypes.sInt64:
unpackType = "<q"
elif __dataType == dataTypes.string:
elif dataType == dataTypes.string:
unpackType = "<s"
elif __dataType == dataTypes.ffloat:
elif dataType == dataTypes.ffloat:
unpackType = "<f"
else:
unpackType = "<B"
# Unpack
return struct.unpack(unpackType, bytes(__data))[0]
return struct.unpack(unpackType, bytes(data))[0]
def packData(__data, __dataType):
def packData(__data, dataType):
"""
Packs data according to dataType
@ -96,11 +90,11 @@ def packData(__data, __dataType):
pack = True # if True, use pack. False only with strings
# Get right pack Type
if __dataType == dataTypes.bbytes:
if dataType == dataTypes.bbytes:
# Bytes, do not use pack, do manually
pack = False
data = __data
elif __dataType == dataTypes.intList:
elif dataType == dataTypes.intList:
# Pack manually
pack = False
# Add length
@ -108,7 +102,7 @@ def packData(__data, __dataType):
# Add all elements
for i in __data:
data += packData(i, dataTypes.sInt32)
elif __dataType == dataTypes.string:
elif dataType == dataTypes.string:
# String, do not use pack, do manually
pack = False
if len(__data) == 0:
@ -119,21 +113,21 @@ def packData(__data, __dataType):
data += b"\x0B"
data += uleb128Encode(len(__data))
data += str.encode(__data, "latin_1", "ignore")
elif __dataType == dataTypes.uInt16:
elif dataType == dataTypes.uInt16:
packType = "<H"
elif __dataType == dataTypes.sInt16:
elif dataType == dataTypes.sInt16:
packType = "<h"
elif __dataType == dataTypes.uInt32:
elif dataType == dataTypes.uInt32:
packType = "<L"
elif __dataType == dataTypes.sInt32:
elif dataType == dataTypes.sInt32:
packType = "<l"
elif __dataType == dataTypes.uInt64:
elif dataType == dataTypes.uInt64:
packType = "<Q"
elif __dataType == dataTypes.sInt64:
elif dataType == dataTypes.sInt64:
packType = "<q"
elif __dataType == dataTypes.string:
elif dataType == dataTypes.string:
packType = "<s"
elif __dataType == dataTypes.ffloat:
elif dataType == dataTypes.ffloat:
packType = "<f"
else:
packType = "<B"
@ -144,7 +138,6 @@ def packData(__data, __dataType):
return data
# TODO: Wat dangerous
def buildPacket(__packet, __packetData = []):
"""
Build a packet
@ -154,7 +147,6 @@ def buildPacket(__packet, __packetData = []):
return -- packet bytes
"""
# Set some variables
packetData = bytes()
packetLength = 0
@ -174,7 +166,6 @@ def buildPacket(__packet, __packetData = []):
packetBytes += packetData # packet data
return packetBytes
def readPacketID(stream):
"""
Read packetID from stream (0-1 bytes)
@ -182,10 +173,8 @@ def readPacketID(stream):
stream -- data stream
return -- packet ID (int)
"""
return unpackData(stream[0:2], dataTypes.uInt16)
def readPacketLength(stream):
"""
Read packet length from stream (3-4-5-6 bytes)
@ -193,7 +182,6 @@ def readPacketLength(stream):
stream -- data stream
return -- packet length (int)
"""
return unpackData(stream[3:7], dataTypes.uInt32)
@ -208,7 +196,6 @@ def readPacketData(stream, structure = [], hasFirstBytes = True):
Optional. Default: True
return -- dictionary. key: name, value: read data
"""
# Read packet ID (first 2 bytes)
data = {}

View File

@ -11,7 +11,6 @@ def checkOldPassword(password, salt, rightPassword):
rightPassword -- right password
return -- bool
"""
return (rightPassword == cryptHelper.crypt(password, "$2y$"+str(base64.b64decode(salt))))
def checkNewPassword(password, dbPassword):

View File

@ -3,8 +3,6 @@ import tornado.web
import tornado.gen
from tornado.ioloop import IOLoop
from objects import glob
from raven.contrib.tornado import SentryMixin
from raven.contrib.tornado import AsyncSentryClient
import gevent
class asyncRequestHandler(tornado.web.RequestHandler):
@ -50,7 +48,6 @@ class asyncRequestHandler(tornado.web.RequestHandler):
return realIP
return self.request.remote_ip
def runBackground(data, callback):
"""
Run a function in the background.
@ -59,7 +56,6 @@ def runBackground(data, callback):
func, args, kwargs = data
def _callback(result):
IOLoop.instance().add_callback(lambda: callback(result))
#glob.pool.apply_async(func, args, kwargs, _callback)
g = gevent.Greenlet(func, *args, **kwargs)
g.link(_callback)
g.start()

View File

@ -15,10 +15,8 @@ def runningUnderUnix():
return --- True if running under UNIX, otherwise False
"""
return True if os.name == "posix" else False
def scheduleShutdown(sendRestartTime, restart, message = "", delay=20):
"""
Schedule a server shutdown/restart
@ -27,7 +25,6 @@ def scheduleShutdown(sendRestartTime, restart, message = "", delay=20):
restart -- if True, server will restart. if False, server will shudown
message -- if set, send that message to every client to warn about the shutdown/restart
"""
# Console output
log.info("Pep.py will {} in {} seconds!".format("restart" if restart else "shutdown", sendRestartTime+delay))
log.info("Sending server restart packets in {} seconds...".format(sendRestartTime))
@ -49,27 +46,23 @@ def scheduleShutdown(sendRestartTime, restart, message = "", delay=20):
# Schedule actual server shutdown/restart some seconds after server restart packet, so everyone gets it
threading.Timer(sendRestartTime+delay, action).start()
def restartServer():
"""Restart pep.py script"""
log.info("Restarting pep.py...")
os.execv(sys.executable, [sys.executable] + sys.argv)
def shutdownServer():
"""Shutdown pep.py"""
log.info("Shutting down pep.py...")
sig = signal.SIGKILL if runningUnderUnix() else signal.CTRL_C_EVENT
os.kill(os.getpid(), sig)
def getSystemInfo():
"""
Get a dictionary with some system/server info
return -- ["unix", "connectedUsers", "webServer", "cpuUsage", "totalMemory", "usedMemory", "loadAverage"]
"""
data = {}
# Get if server is running under unix/nt

View File

@ -14,9 +14,8 @@ def getID(username):
username -- user
return -- user id or False
"""
# Get user ID from db
userID = glob.db.fetch("SELECT id FROM users WHERE username = %s", [username])
userID = glob.db.fetch("SELECT id FROM users WHERE username = %s LIMIT 1", [username])
# Make sure the query returned something
if userID == None:
@ -25,7 +24,6 @@ def getID(username):
# Return user ID
return userID["id"]
def checkLogin(userID, password):
"""
Check userID's login with specified password
@ -35,9 +33,8 @@ def checkLogin(userID, password):
password -- plain md5 password
return -- True or False
"""
# Get password data
passwordData = glob.db.fetch("SELECT password_md5, salt, password_version FROM users WHERE id = %s", [userID])
passwordData = glob.db.fetch("SELECT password_md5, salt, password_version FROM users WHERE id = %s LIMIT 1", [userID])
# Make sure the query returned something
if passwordData == None:
@ -51,8 +48,7 @@ def checkLogin(userID, password):
ok = passwordHelper.checkOldPassword(password, passwordData["salt"], passwordData["password_md5"])
if not ok: return False
newpass = passwordHelper.genBcrypt(password)
glob.db.execute("UPDATE users SET password_md5=%s, salt='', password_version='2' WHERE id = %s", [newpass, userID])
glob.db.execute("UPDATE users SET password_md5=%s, salt='', password_version='2' WHERE id = %s LIMIT 1", [newpass, userID])
def exists(userID):
"""
@ -61,8 +57,7 @@ def exists(userID):
userID -- user ID to check
return -- bool
"""
result = glob.db.fetch("SELECT id FROM users WHERE id = %s", [userID])
result = glob.db.fetch("SELECT id FROM users WHERE id = %s LIMIT 1", [userID])
if result == None:
return False
else:
@ -76,8 +71,7 @@ def getSilenceEnd(userID):
userID -- userID
return -- UNIX time
"""
return glob.db.fetch("SELECT silence_end FROM users WHERE id = %s", [userID])["silence_end"]
return glob.db.fetch("SELECT silence_end FROM users WHERE id = %s LIMIT 1", [userID])["silence_end"]
def silence(userID, seconds, silenceReason, author = 999):
@ -91,7 +85,7 @@ def silence(userID, seconds, silenceReason, author = 999):
"""
# db qurey
silenceEndTime = int(time.time())+seconds
glob.db.execute("UPDATE users SET silence_end = %s, silence_reason = %s WHERE id = %s", [silenceEndTime, silenceReason, userID])
glob.db.execute("UPDATE users SET silence_end = %s, silence_reason = %s WHERE id = %s LIMIT 1", [silenceEndTime, silenceReason, userID])
# Loh
targetUsername = getUsername(userID)
@ -109,9 +103,8 @@ def getRankedScore(userID, gameMode):
gameMode -- int value, see gameModes
return -- ranked score
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT ranked_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["ranked_score_"+modeForDB]
return glob.db.fetch("SELECT ranked_score_"+modeForDB+" FROM users_stats WHERE id = %s LIMIT 1", [userID])["ranked_score_"+modeForDB]
def getTotalScore(userID, gameMode):
@ -122,10 +115,8 @@ def getTotalScore(userID, gameMode):
gameMode -- int value, see gameModes
return -- total score
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT total_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["total_score_"+modeForDB]
return glob.db.fetch("SELECT total_score_"+modeForDB+" FROM users_stats WHERE id = %s LIMIT 1", [userID])["total_score_"+modeForDB]
def getAccuracy(userID, gameMode):
"""
@ -135,10 +126,8 @@ def getAccuracy(userID, gameMode):
gameMode -- int value, see gameModes
return -- accuracy
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT avg_accuracy_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["avg_accuracy_"+modeForDB]
return glob.db.fetch("SELECT avg_accuracy_"+modeForDB+" FROM users_stats WHERE id = %s LIMIT 1", [userID])["avg_accuracy_"+modeForDB]
def getGameRank(userID, gameMode):
"""
@ -150,13 +139,12 @@ def getGameRank(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
result = glob.db.fetch("SELECT position FROM leaderboard_"+modeForDB+" WHERE user = %s", [userID])
result = glob.db.fetch("SELECT position FROM leaderboard_"+modeForDB+" WHERE user = %s LIMIT 1", [userID])
if result == None:
return 0
else:
return result["position"]
def getPlaycount(userID, gameMode):
"""
Get userID's playcount relative to gameMode
@ -167,8 +155,7 @@ def getPlaycount(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT playcount_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["playcount_"+modeForDB]
return glob.db.fetch("SELECT playcount_"+modeForDB+" FROM users_stats WHERE id = %s LIMIT 1", [userID])["playcount_"+modeForDB]
def getUsername(userID):
"""
@ -178,8 +165,7 @@ def getUsername(userID):
return -- username
"""
return glob.db.fetch("SELECT username FROM users WHERE id = %s", [userID])["username"]
return glob.db.fetch("SELECT username FROM users WHERE id = %s LIMIT 1", [userID])["username"]
def getFriendList(userID):
"""
@ -202,7 +188,6 @@ def getFriendList(userID):
# Return friend IDs
return friends
def addFriend(userID, friendID):
"""
Add friendID to userID's friend list
@ -216,7 +201,7 @@ def addFriend(userID, friendID):
return
# check user isn't already a friend of ours
if glob.db.fetch("SELECT id FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID]) != None:
if glob.db.fetch("SELECT id FROM users_relationships WHERE user1 = %s AND user2 = %s LIMIT 1", [userID, friendID]) != None:
return
# Set new value
@ -230,7 +215,6 @@ def removeFriend(userID, friendID):
userID -- user
friendID -- old friend
"""
# Delete user relationship. We don't need to check if the relationship was there, because who gives a shit,
# if they were not friends and they don't want to be anymore, be it. ¯\_(ツ)_/¯
glob.db.execute("DELETE FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID])
@ -245,8 +229,7 @@ def getCountry(userID):
userID -- user
return -- country code (two letters)
"""
return glob.db.fetch("SELECT country FROM users_stats WHERE id = %s", [userID])["country"]
return glob.db.fetch("SELECT country FROM users_stats WHERE id = %s LIMIT 1", [userID])["country"]
def getPP(userID, gameMode):
"""
@ -257,7 +240,7 @@ def getPP(userID, gameMode):
"""
modeForDB = gameModes.getGameModeForDB(gameMode)
return glob.db.fetch("SELECT pp_{} FROM users_stats WHERE id = %s".format(modeForDB), [userID])["pp_{}".format(modeForDB)]
return glob.db.fetch("SELECT pp_{} FROM users_stats WHERE id = %s LIMIT 1".format(modeForDB), [userID])["pp_{}".format(modeForDB)]
def setCountry(userID, country):
"""
@ -266,7 +249,7 @@ def setCountry(userID, country):
userID -- userID
country -- country letters
"""
glob.db.execute("UPDATE users_stats SET country = %s WHERE id = %s", [country, userID])
glob.db.execute("UPDATE users_stats SET country = %s WHERE id = %s LIMIT 1", [country, userID])
def getShowCountry(userID):
"""
@ -275,7 +258,7 @@ def getShowCountry(userID):
userID -- userID
return -- True if country is shown, False if it's hidden
"""
country = glob.db.fetch("SELECT show_country FROM users_stats WHERE id = %s", [userID])
country = glob.db.fetch("SELECT show_country FROM users_stats WHERE id = %s LIMIT 1", [userID])
if country == None:
return False
return generalFunctions.stringToBool(country)
@ -292,25 +275,42 @@ def saveBanchoSession(userID, ip):
"""
Save userid and ip of this token in bancho_sessions table.
Used to cache logins on LETS requests
userID --
ip -- user's ip address
"""
log.debug("Saving bancho session ({}::{}) in db".format(userID, ip))
glob.db.execute("INSERT INTO bancho_sessions (id, userid, ip) VALUES (NULL, %s, %s)", [userID, ip])
def deleteBanchoSessions(userID, ip):
"""Delete this bancho session from DB"""
log.debug("Deleting bancho session ({}::{}) from db".format(userID, ip))
"""
Delete this bancho session from DB
userID --
ip -- user's IP address
"""
try:
glob.db.execute("DELETE FROM bancho_sessions WHERE userid = %s AND ip = %s", [userID, ip])
except:
log.warning("Token for user: {} ip: {} doesn't exist".format(userID, ip))
def is2FAEnabled(userID):
"""Returns True if 2FA is enable for this account"""
"""
Check if 2FA is enabled on an account
userID --
return -- True if 2FA is enabled, False if 2FA is disabled
"""
result = glob.db.fetch("SELECT id FROM 2fa_telegram WHERE userid = %s LIMIT 1", [userID])
return True if result is not None else False
def check2FA(userID, ip):
"""Returns True if this IP is untrusted"""
"""
Check if an ip is trusted
userID --
ip -- user's IP address
return -- True if the IP is untrusted, False if it's trusted
"""
if is2FAEnabled(userID) == False:
return False
@ -353,7 +353,7 @@ def isAllowed(userID):
userID -- id of the user
return -- True if not banned or restricted, otherwise false.
"""
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s LIMIT 1", [userID])
if result != None:
return (result["privileges"] & privileges.USER_NORMAL) and (result["privileges"] & privileges.USER_PUBLIC)
else:
@ -366,7 +366,7 @@ def isRestricted(userID):
userID -- id of the user
return -- True if not restricted, otherwise false.
"""
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s LIMIT 1", [userID])
if result != None:
return (result["privileges"] & privileges.USER_NORMAL) and not (result["privileges"] & privileges.USER_PUBLIC)
else:
@ -379,7 +379,7 @@ def isBanned(userID):
userID -- id of the user
return -- True if not banned, otherwise false.
"""
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s LIMIT 1", [userID])
if result != None:
return not (result["privileges"] & 3 > 0)
else:
@ -392,7 +392,7 @@ def ban(userID):
userID -- id of user
"""
banDateTime = int(time.time())
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s", [ ~(privileges.USER_NORMAL | privileges.USER_PUBLIC | privileges.USER_PENDING_VERIFICATION) , banDateTime, userID])
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s LIMIT 1", [ ~(privileges.USER_NORMAL | privileges.USER_PUBLIC | privileges.USER_PENDING_VERIFICATION) , banDateTime, userID])
def unban(userID):
"""
@ -400,7 +400,7 @@ def unban(userID):
userID -- id of user
"""
glob.db.execute("UPDATE users SET privileges = privileges | %s, ban_datetime = 0 WHERE id = %s", [ (privileges.USER_NORMAL | privileges.USER_PUBLIC) , userID])
glob.db.execute("UPDATE users SET privileges = privileges | %s, ban_datetime = 0 WHERE id = %s LIMIT 1", [ (privileges.USER_NORMAL | privileges.USER_PUBLIC) , userID])
def restrict(userID):
"""
@ -409,7 +409,7 @@ def restrict(userID):
userID -- id of user
"""
banDateTime = int(time.time())
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s", [~privileges.USER_PUBLIC, banDateTime, userID])
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s LIMIT 1", [~privileges.USER_PUBLIC, banDateTime, userID])
def unrestrict(userID):
"""
@ -427,7 +427,7 @@ def getPrivileges(userID):
userID -- id of user
return -- privileges number
"""
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s LIMIT 1", [userID])
if result != None:
return result["privileges"]
else:
@ -440,10 +440,10 @@ def setPrivileges(userID, priv):
userID -- id of user
priv -- privileges number
"""
glob.db.execute("UPDATE users SET privileges = %s WHERE id = %s", [priv, userID])
glob.db.execute("UPDATE users SET privileges = %s WHERE id = %s LIMIT 1", [priv, userID])
def isInPrivilegeGroup(userID, groupName):
groupPrivileges = glob.db.fetch("SELECT privileges FROM privileges_groups WHERE name = %s", [groupName])
groupPrivileges = glob.db.fetch("SELECT privileges FROM privileges_groups WHERE name = %s LIMIT 1", [groupName])
if groupPrivileges == None:
return False
groupPrivileges = groupPrivileges["privileges"]
@ -465,7 +465,7 @@ def appendNotes(userID, notes, addNl = True):
"""
if addNl == True:
notes = "\n"+notes
glob.db.execute("UPDATE users SET notes=CONCAT(COALESCE(notes, ''),%s) WHERE id = %s", [notes, userID])
glob.db.execute("UPDATE users SET notes=CONCAT(COALESCE(notes, ''),%s) WHERE id = %s LIMIT 1", [notes, userID])
def logHardware(userID, hashes, activation = False):

View File

@ -19,7 +19,7 @@ from objects import glob
from helpers import chatHelper as chat
import raven
class Client:
class Client():
"""
IRC Client object
"""
@ -43,6 +43,7 @@ class Client:
self.socket = sock
(self.ip, self.port) = sock.getpeername()
self.username = ""
self.banchoUsername = ""
self.supposedUsername = ""
self.joinedChannels = []
@ -275,12 +276,12 @@ class Client:
return
# Make sure the IRC token was correct:
if nick.lower() != self.supposedUsername.lower():
if nick.lower() != chat.fixUsernameForIRC(self.supposedUsername.lower()):
self.reply("464 :Password incorrect")
return
# Make sure we are not connected to Bancho
token = glob.tokens.getTokenFromUsername(nick)
token = glob.tokens.getTokenFromUsername(chat.fixUsernameForBancho(nick))
if token != None:
self.reply("433 * {} :Nickname is already in use".format(nick))
return
@ -293,6 +294,7 @@ class Client:
# Everything seems fine, set username (nickname)
self.username = nick
self.banchoUsername = chat.fixUsernameForBancho(self.username)
elif command == "USER":
# Ignore USER command, we use nickname only
return
@ -307,7 +309,7 @@ class Client:
# If we now have a valid username, connect to bancho and send IRC welcome stuff
if self.username != "":
# Bancho connection
chat.IRCConnect(self.username)
chat.IRCConnect(self.banchoUsername)
# IRC reply
self.replyCode(1, "Welcome to the Internet Relay Network")
@ -329,7 +331,7 @@ class Client:
return
# Get bancho token object
token = glob.tokens.getTokenFromUsername(self.username)
token = glob.tokens.getTokenFromUsername(self.banchoUsername)
if token == None:
return
@ -354,7 +356,7 @@ class Client:
continue
# Attempt to join the channel
response = chat.IRCJoinChannel(self.username, channel)
response = chat.IRCJoinChannel(self.banchoUsername, channel)
if response == 0:
# Joined successfully
self.joinedChannels.append(channel)
@ -376,7 +378,7 @@ class Client:
token = glob.tokens.getTokenFromUserID(user)
if token == None:
continue
usernames.append(token.username)
usernames.append(chat.fixUsernameForIRC(token.username))
usernames = " ".join(usernames)
# Send IRC users lis
@ -394,7 +396,7 @@ class Client:
return
# Get bancho token object
token = glob.tokens.getTokenFromUsername(self.username)
token = glob.tokens.getTokenFromUsername(self.banchoUsername)
if token == None:
return
@ -409,7 +411,7 @@ class Client:
continue
# Attempt to part the channel
response = chat.IRCPartChannel(self.username, channel)
response = chat.IRCPartChannel(self.banchoUsername, channel)
if response == 0:
# No errors, remove channel from joinedChannels
self.joinedChannels.remove(channel)
@ -433,7 +435,7 @@ class Client:
message = arguments[1]
# Send the message to bancho and reply
response = chat.sendMessage(self.username, recipient, message, toIRC=False)
response = chat.sendMessage(self.banchoUsername, recipient, message, toIRC=False)
if response == 404:
self.replyCode(404, "Cannot send to channel", channel=recipient)
return
@ -453,7 +455,6 @@ class Client:
for _, value in self.server.clients.items():
if recipient in value.joinedChannels and value != self:
value.message(":{} PRIVMSG {} :{}".format(self.username, recipient, message))
#self.messageChannel(recipient, command, "{} :{}".format(recipient, message))
else:
# Private message (IRC)
for _, value in self.server.clients.items():
@ -513,7 +514,7 @@ class Client:
class Server:
class Server():
def __init__(self, port):
self.host = socket.getfqdn("127.0.0.1")[:63]
self.port = port
@ -529,7 +530,7 @@ class Server:
for _, value in self.clients.items():
if value.username == username:
value.disconnect(callLogout=False)
break# or dictionary changes size during iteration
break # or dictionary changes size during iteration
def banchoJoinChannel(self, username, channel):
"""
@ -538,6 +539,7 @@ class Server:
username -- username of bancho user
channel -- joined channel name
"""
username = chat.fixUsernameForIRC(username)
for _, value in self.clients.items():
if channel in value.joinedChannels:
value.message(":{} JOIN {}".format(username, channel))
@ -549,6 +551,7 @@ class Server:
username -- username of bancho user
channel -- joined channel name
"""
username = chat.fixUsernameForIRC(username)
for _, value in self.clients.items():
if channel in value.joinedChannels:
value.message(":{} PART {}".format(username, channel))
@ -561,6 +564,8 @@ class Server:
to -- receiver username
message -- text of the message
"""
fro = chat.fixUsernameForIRC(fro)
to = chat.fixUsernameForIRC(to)
if to.startswith("#"):
# Public message
for _, value in self.clients.items():

View File

@ -1,7 +1,8 @@
# TODO: Rewrite this shit
from objects import glob
from helpers import generalFunctions
class banchoConfig:
class banchoConfig():
"""
Class that loads settings from bancho_settings db table
"""
@ -12,31 +13,30 @@ class banchoConfig:
"""
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]
loadFromDB -- if True, load values from db. If False, don't load values. Optional.
"""
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 = %s WHERE name = 'bancho_maintenance'", [int(maintenance)])

View File

@ -1,6 +1,6 @@
from objects import glob
class channel:
class channel():
"""
A chat channel
"""
@ -16,7 +16,6 @@ class channel:
temp -- if True, channel will be deleted when there's no one in the channel
hidden -- if True, channel won't be shown in channels list
"""
self.name = name
self.description = description
self.publicRead = publicRead
@ -33,25 +32,21 @@ class channel:
elif self.name.startswith("#multi_"):
self.clientName = "#multiplayer"
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
"""
if userID in self.connectedUsers:
self.connectedUsers.remove(userID)
@ -60,7 +55,6 @@ class channel:
if self.temp == True and ((l == 0) or (l == 1 and 999 in self.connectedUsers)):
glob.channels.removeChannel(self.name)
def getConnectedUsers(self):
"""
Get connected user IDs list
@ -69,7 +63,6 @@ class channel:
"""
return self.connectedUsers
def getConnectedUsersCount(self):
"""
Count connected users

View File

@ -2,16 +2,14 @@ from objects import glob
from objects import channel
from helpers import logHelper as log
class channelList:
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
@ -27,7 +25,6 @@ class channelList:
publicWrite = True if i["public_write"] == 1 else False
self.addChannel(i["name"], i["description"], publicRead, publicWrite)
def addChannel(self, name, description, publicRead, publicWrite, temp = False, hidden = False):
"""
Add a channel object to channels dictionary
@ -42,7 +39,6 @@ class channelList:
self.channels[name] = channel.channel(name, description, publicRead, publicWrite, temp, hidden)
log.info("Created channel {}".format(name))
def addTempChannel(self, name):
"""
Add a temporary channel (like #spectator or #multiplayer), gets deleted when there's no one in the channel
@ -56,7 +52,6 @@ class channelList:
self.channels[name] = channel.channel(name, "Chat", True, True, True, True)
log.info("Created temp channel {}".format(name))
def removeChannel(self, name):
"""
Removes a channel from channels list

View File

@ -1,4 +1,4 @@
class chatFilters:
class chatFilters():
def __init__(self, fileName="filters.txt"):
self.filters = {}
self.loadFilters(fileName)

View File

@ -17,12 +17,10 @@ def connect():
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)
@ -57,4 +55,4 @@ def fokabotResponse(fro, chan, message):
return i["callback"](fro, chan, message[1:])
# No commands triggered
return False
return False

View File

@ -10,8 +10,20 @@ from constants import dataTypes
from constants import matchTeams
from helpers import logHelper as log
from helpers import chatHelper as chat
from helpers import generalFunctions
import copy
class match:
class slot():
def __init__(self):
self.status = slotStatuses.free
self.team = 0
self.userID = -1
self.mods = 0
self.loaded = False
self.skip = False
self.complete = False
class match():
"""Multiplayer match object"""
matchID = 0
inProgress = False
@ -21,7 +33,7 @@ class match:
beatmapName = ""
beatmapID = 0
beatmapMD5 = ""
slots = [] # list of dictionaries {"status": 0, "team": 0, "userID": -1, "mods": 0, "loaded": False, "skip": False, "complete": False}
slots = []
hostUserID = 0
gameMode = gameModes.std
matchScoringType = matchScoringTypes.score
@ -46,7 +58,10 @@ class match:
self.inProgress = False
self.mods = 0
self.matchName = matchName
self.matchPassword = matchPassword
if matchPassword != "":
self.matchPassword = generalFunctions.stringMd5(matchPassword)
else:
self.matchPassword = ""
self.beatmapID = beatmapID
self.beatmapName = beatmapName
self.beatmapMD5 = beatmapMD5
@ -60,12 +75,11 @@ class match:
# 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})
self.slots.append(slot())
# Create #multiplayer channel
glob.channels.addTempChannel("#multi_{}".format(self.matchID))
def getMatchData(self):
"""
Return binary match data structure for packetHelper
@ -85,15 +99,15 @@ class match:
# Slots status IDs, always 16 elements
for i in range(0,16):
struct.append([self.slots[i]["status"], dataTypes.byte])
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])
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"]
uid = self.slots[i].userID
if uid > -1:
struct.append([uid, dataTypes.uInt32])
@ -109,15 +123,13 @@ class match:
# 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])
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
@ -149,26 +161,25 @@ class match:
If Null is passed, that value won't be edited
"""
if slotStatus != None:
self.slots[slotID]["status"] = slotStatus
self.slots[slotID].status = slotStatus
if slotTeam != None:
self.slots[slotID]["team"] = slotTeam
self.slots[slotID].team = slotTeam
if slotUserID != None:
self.slots[slotID]["userID"] = slotUserID
self.slots[slotID].userID = slotUserID
if slotMods != None:
self.slots[slotID]["mods"] = slotMods
self.slots[slotID].mods = slotMods
if slotLoaded != None:
self.slots[slotID]["loaded"] = slotLoaded
self.slots[slotID].loaded = slotLoaded
if slotSkip != None:
self.slots[slotID]["skip"] = slotSkip
self.slots[slotID].skip = slotSkip
if slotComplete != None:
self.slots[slotID]["complete"] = slotComplete
self.slots[slotID].complete = slotComplete
def setSlotMods(self, slotID, mods):
"""
@ -182,7 +193,6 @@ class match:
self.sendUpdate()
log.info("MPROOM{}: Slot{} mods changed to {}".format(self.matchID, slotID, mods))
def toggleSlotReady(self, slotID):
"""
Switch slotID ready/not ready status
@ -191,14 +201,14 @@ class match:
slotID -- slot number
"""
# Update ready status and setnd update
oldStatus = self.slots[slotID]["status"]
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()
log.info("MPROOM{}: Slot{} changed ready status to {}".format(self.matchID, slotID, self.slots[slotID]["status"]))
log.info("MPROOM{}: Slot{} changed ready status to {}".format(self.matchID, slotID, self.slots[slotID].status))
def toggleSlotLock(self, slotID):
"""
@ -208,13 +218,13 @@ class match:
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"])
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:
if self.slots[slotID].status == slotStatuses.locked:
newStatus = slotStatuses.free
else:
newStatus = slotStatuses.locked
@ -240,33 +250,31 @@ class match:
return
# Set loaded to True
self.slots[slotID]["loaded"] = True
self.slots[slotID].loaded = True
log.info("MPROOM{}: User {} loaded".format(self.matchID, userID))
# Check all loaded
total = 0
loaded = 0
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.playing:
if self.slots[i].status == slotStatuses.playing:
total+=1
if self.slots[i]["loaded"] == True:
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 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())
log.info("MPROOM{}: All players loaded! Match starting...".format(self.matchID))
def playerSkip(self, userID):
"""
Set a player skip status to True
@ -278,13 +286,13 @@ class match:
return
# Set skip to True
self.slots[slotID]["skip"] = True
self.slots[slotID].skip = True
log.info("MPROOM{}: User {} skipped".format(self.matchID, userID))
# Send skip packet to every playing useR
# Send skip packet to every playing user
for i in range(0,16):
uid = self.slots[i]["userID"]
if self.slots[i]["status"] == slotStatuses.playing and uid > -1:
uid = self.slots[i].userID
if (self.slots[i].status & slotStatuses.playing > 0) and uid > -1:
token = glob.tokens.getTokenFromUserID(uid)
if token != None:
token.enqueue(serverPackets.playerSkipped(uid))
@ -293,9 +301,9 @@ class match:
total = 0
skipped = 0
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.playing:
if self.slots[i].status == slotStatuses.playing:
total+=1
if self.slots[i]["skip"] == True:
if self.slots[i].skip == True:
skipped+=1
if total == skipped:
@ -304,8 +312,8 @@ class match:
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 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())
@ -329,9 +337,9 @@ class match:
total = 0
completed = 0
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.playing:
if self.slots[i].status == slotStatuses.playing:
total+=1
if self.slots[i]["complete"] == True:
if self.slots[i].complete == True:
completed+=1
if total == completed:
@ -345,38 +353,34 @@ class match:
# 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
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 self.slots[i].userID > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID)
if token != None:
token.enqueue(serverPackets.matchComplete())
# Console output
log.info("MPROOM{}: Match completed".format(self.matchID))
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:
if self.slots[i].userID == userID:
return i
return None
def userJoin(self, userID):
@ -389,7 +393,7 @@ class match:
# Find first free slot
for i in range(0,16):
if self.slots[i]["status"] == slotStatuses.free:
if self.slots[i].status == slotStatuses.free:
# Occupy slot
self.setSlot(i, slotStatuses.notReady, 0, userID, 0)
@ -409,7 +413,6 @@ class match:
userID -- user if of the user
"""
# Make sure the user is in room
slotID = self.getUserSlotID(userID)
if slotID == None:
@ -429,7 +432,7 @@ class match:
if userID == self.hostUserID:
# Give host to someone else
for i in range(0,16):
uid = self.slots[i]["userID"]
uid = self.slots[i].userID
if uid > -1:
self.setHost(uid)
break
@ -440,7 +443,6 @@ class match:
# Console output
log.info("MPROOM{}: {} left the room".format(self.matchID, userID))
def userChangeSlot(self, userID, newSlotID):
"""
Change userID slot to newSlotID
@ -448,24 +450,23 @@ class match:
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:
if self.slots[newSlotID].userID > -1 and self.slots[newSlotID].status != slotStatuses.free:
return
# Get old slot data
oldData = self.slots[oldSlotID].copy()
oldData = copy.deepcopy(self.slots[oldSlotID])
# Free old slot
self.setSlot(oldSlotID, slotStatuses.free, 0, -1, 0)
# Occupy new slot
self.setSlot(newSlotID, oldData["status"], oldData["team"], userID, oldData["mods"])
self.setSlot(newSlotID, oldData.status, oldData.team, oldData.userID, oldData.mods)
# Send updated match data
self.sendUpdate()
@ -479,12 +480,15 @@ class match:
newPassword -- new password string
"""
self.matchPassword = newPassword
if newPassword != "":
self.matchPassword = generalFunctions.stringMd5(newPassword)
else:
self.matchPassword = ""
# 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 self.slots[i].userID > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID)
if token != None:
token.enqueue(serverPackets.changeMatchPassword(self.matchPassword))
@ -494,7 +498,6 @@ class match:
# Console output
log.info("MPROOM{}: Password changed to {}".format(self.matchID, self.matchPassword))
def changeMatchMods(self, mods):
"""
Set match global mods
@ -531,7 +534,7 @@ class match:
slotID -- ID of slot
"""
# Make sure there is someone in that slot
uid = self.slots[slotID]["userID"]
uid = self.slots[slotID].userID
if uid == -1:
return
@ -541,7 +544,6 @@ class match:
# Send updates
self.sendUpdate()
def playerFailed(self, userID):
"""
Send userID's failed packet to everyone in match
@ -555,7 +557,7 @@ class match:
# Send packet to everyone
for i in range(0,16):
uid = self.slots[i]["userID"]
uid = self.slots[i].userID
if uid > -1:
token = glob.tokens.getTokenFromUserID(uid)
if token != None:
@ -564,7 +566,6 @@ class match:
# Console output
log.info("MPROOM{}: {} has failed!".format(self.matchID, userID))
def invite(self, fro, to):
"""
Fro invites to in this match.
@ -587,19 +588,16 @@ class match:
message = "Come join my multiplayer match: \"[osump://{}/{} {}]\"".format(self.matchID, self.matchPassword.replace(" ", "_"), self.matchName)
chat.sendMessage(token=froToken, to=toToken.username, message=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:
if self.slots[i].userID > -1:
c+=1
return c
def changeTeam(self, userID):
@ -614,15 +612,15 @@ class match:
return
# Update slot and send update
newTeam = matchTeams.blue if self.slots[slotID]["team"] == matchTeams.red else matchTeams.red
newTeam = matchTeams.blue if self.slots[slotID].team == matchTeams.red else matchTeams.red
self.setSlot(slotID, None, newTeam)
self.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 self.slots[i].userID > -1:
token = glob.tokens.getTokenFromUserID(self.slots[i].userID)
if token != None:
token.enqueue(serverPackets.updateMatch(self.matchID))
@ -645,10 +643,10 @@ class match:
# 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 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"]:
firstTeam = self.slots[i].team
elif firstTeam != self.slots[i].team:
log.info("MPROOM{}: Teams are valid".format(self.matchID))
return True

View File

@ -2,7 +2,7 @@ from objects import match
from objects import glob
from constants import serverPackets
class matchList:
class matchList():
matches = {}
usersInLobby = []
lastID = 1
@ -32,49 +32,43 @@ class matchList:
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):
def disposeMatch(self, matchID):
"""
Destroy match object with id = __matchID
Destroy match object with id = matchID
__matchID -- ID of match to dispose
matchID -- ID of match to dispose
"""
# Make sure the match exists
if __matchID not in self.matches:
if matchID not in self.matches:
return
# Remove match object
self.matches.pop(__matchID)
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))
token.enqueue(serverPackets.disposeMatch(matchID))

View File

@ -8,33 +8,9 @@ from objects import glob
import uuid
import time
import threading
from helpers import logHelper as log
from helpers import chatHelper as chat
class token:
"""
Osu Token object
token -- token string
userID -- userID associated to that token
username -- username 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
latestTillerino -- beatmap ID of latest song from tillerino bot
"""
class token():
def __init__(self, userID, token = None, ip = "", irc = False, timeOffset = 0):
"""
Create a token object and set userID and token
@ -44,8 +20,8 @@ class token:
if not passed, token will be generated
ip -- client ip. optional.
irc -- if True, set this token as IRC client. optional.
timeOffset -- the time offset from UTC for this user. optional.
"""
# Set stuff
self.userID = userID
self.username = userHelper.getUsername(self.userID)
@ -71,7 +47,6 @@ class token:
self.tillerino = [0,0,-1.0] # beatmap, mods, acc
self.silenceEndTime = 0
self.queue = bytes()
self.osuDirectAlert = False # NOTE: Remove this when osu!direct will be fixed
# Spam protection
self.spamRate = 0
@ -102,14 +77,14 @@ class token:
if ip != "":
userHelper.saveBanchoSession(self.userID, self.ip)
def enqueue(self, __bytes):
def enqueue(self, bytes):
"""
Add bytes (packets) to queue
__bytes -- (packet) bytes to enqueue
bytes -- (packet) bytes to enqueue
"""
if self.irc == False:
self.queue += __bytes
self.queue += bytes
def resetQueue(self):
@ -117,95 +92,140 @@ class token:
self.queue = bytes()
def joinChannel(self, __channel):
"""Add __channel to joined channels list
def joinChannel(self, channel):
"""
Add channel to joined channels list
__channel -- channel name"""
channel -- channel name
"""
if channel not in self.joinedChannels:
self.joinedChannels.append(channel)
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 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 setLocation(self, location):
"""
Set location (latitude and longitude)
location -- [latitude, longitude]
"""
self.location = location
def getLatitude(self):
"""Get latitude
return -- latitude"""
"""
Get latitude
return -- latitude
"""
return self.location[0]
def getLongitude(self):
"""Get longitude
"""
Get longitude
return -- longitude"""
return -- longitude
"""
return self.location[1]
def startSpectating(self, userID):
"""Set the spectating user to userID
"""
Set the spectating user to userID
userID -- target userID"""
userID -- target userID
"""
self.spectating = userID
def stopSpectating(self):
"""Set the spectating user to 0, aka no user"""
# Remove our userID from host's spectators
target = self.spectating
targetToken = glob.tokens.getTokenFromUserID(target)
if targetToken != None:
# Remove us from host's spectators list
targetToken.removeSpectator(self.userID)
# Send the spectator left packet to host
targetToken.enqueue(serverPackets.removeSpectator(self.userID))
for c in targetToken.spectators:
spec = glob.tokens.getTokenFromUserID(c)
spec.enqueue(serverPackets.fellowSpectatorLeft(self.userID))
# If nobody is spectating the host anymore, close #spectator channel
if len(targetToken.spectators) == 0:
chat.partChannel(token=targetToken, channel="#spect_{}".format(target), kick=True)
# Part #spectator channel
chat.partChannel(token=self, channel="#spect_{}".format(target), kick=True)
# Set our spectating user to 0
self.spectating = 0
# Console output
log.info("{} are no longer spectating {}".format(self.username, target))
def partMatch(self):
# Make sure we are in a match
if self.matchID == -1:
return
# Part #multiplayer channel
chat.partChannel(token=self, channel="#multi_{}".format(self.matchID), kick=True)
# Make sure the match exists
if self.matchID not in glob.matches.matches:
return
# The match exists, get object
match = glob.matches.matches[self.matchID]
# Set slot to free
match.userLeft(self.userID)
# Set usertoken match to -1
self.matchID = -1
def addSpectator(self, userID):
"""Add userID to our spectators
userID -- new spectator 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 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
def setCountry(self, __countryID):
"""Set country to __countryID
__countryID -- numeric country ID. See countryHelper.py"""
self.country = __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"""
"""
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())
@ -214,23 +234,24 @@ class token:
"""Set a new away message"""
self.awayMessage = __awayMessage
def joinMatch(self, __matchID):
def joinMatch(self, matchID):
"""
Set match to matchID
__matchID -- new match ID
matchID -- new match ID
"""
self.matchID = __matchID
def partMatch(self):
"""Set match to -1"""
self.matchID = -1
self.matchID = matchID
def kick(self, message="You have been kicked from the server. Please login again."):
"""Kick this user from the server"""
"""
Kick this user from the server
message -- Notification message to send to this user. Optional.
"""
# Send packet to target
log.info("{} has been disconnected. (kick)".format(self.username))
self.enqueue(serverPackets.notification(message))
if message != "":
self.enqueue(serverPackets.notification(message))
self.enqueue(serverPackets.loginFailed())
# Logout event

View File

@ -3,10 +3,9 @@ from objects import glob
import time
import threading
from events import logoutEvent
from helpers import logHelper as log
from helpers import userHelper
class tokenList:
class tokenList():
"""
List of connected osu tokens
@ -27,7 +26,6 @@ class tokenList:
irc -- if True, set this token as IRC client
return -- token object
"""
newToken = osuToken.token(userID, ip=ip, irc=irc, timeOffset=timeOffset)
self.tokens[newToken.token] = newToken
return newToken
@ -38,7 +36,6 @@ class tokenList:
token -- token string
"""
if token in self.tokens:
# Delete session from DB
if self.tokens[token].ip != "":
@ -47,16 +44,13 @@ class tokenList:
# Pop token from list
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
return -- false if not found, userID if found
"""
# Make sure the token exists
if token not in self.tokens:
return False
@ -64,7 +58,6 @@ class tokenList:
# Get userID associated to that token
return self.tokens[token].userID
def getTokenFromUserID(self, userID):
"""
Get token from a user ID
@ -72,7 +65,6 @@ class tokenList:
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:
@ -81,7 +73,6 @@ class tokenList:
# Return none if not found
return None
def getTokenFromUsername(self, username):
"""
Get token from a username
@ -89,7 +80,6 @@ class tokenList:
username -- username to find
return -- False if not found, token object if found
"""
# lowercase
who = username.lower()
@ -101,7 +91,6 @@ class tokenList:
# Return none if not found
return None
def deleteOldTokens(self, userID):
"""
Delete old userID's tokens if found
@ -114,11 +103,6 @@ class tokenList:
if value.userID == userID:
# Delete this token from the dictionary
self.tokens[key].kick("You have logged in from somewhere else. You can't connect to Bancho/IRC from more than one device at the same time.")
#self.tokens.pop(key)
# break or items() function throws errors
#break
def multipleEnqueue(self, packet, who, but = False):
"""
@ -128,7 +112,6 @@ class tokenList:
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:
@ -145,22 +128,20 @@ class tokenList:
packet -- packet bytes to enqueue
"""
for _, value in self.tokens.items():
value.enqueue(packet)
def usersTimeoutCheckLoop(self, __timeoutTime = 100, __checkTime = 100):
def usersTimeoutCheckLoop(self, timeoutTime = 100, checkTime = 100):
"""
Deletes all timed out users.
If called once, will recall after __checkTime seconds and so on, forever
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)
timeoutTime - seconds of inactivity required to disconnect someone (Default: 100)
checkTime - seconds between loops (Default: 100)
"""
timedOutTokens = [] # timed out users
timeoutLimit = time.time()-__timeoutTime
timeoutLimit = time.time()-timeoutTime
for key, value in self.tokens.items():
# Check timeout (fokabot is ignored)
if value.pingTime < timeoutLimit and value.userID != 999 and value.irc == False:
@ -174,15 +155,13 @@ class tokenList:
logoutEvent.handle(self.tokens[i], None)
# Schedule a new check (endless loop)
threading.Timer(__checkTime, self.usersTimeoutCheckLoop, [__timeoutTime, __checkTime]).start()
threading.Timer(checkTime, self.usersTimeoutCheckLoop, [timeoutTime, checkTime]).start()
def spamProtectionResetLoop(self):
"""
Reset spam rate every 10 seconds.
CALL THIS FUNCTION ONLY ONCE!
"""
#log.debug("Resetting spam protection...")
# Reset spamRate for every token
for _, value in self.tokens.items():
value.spamRate = 0