Working on Matchmaking

This commit is contained in:
ZKWolf 2023-07-19 22:29:52 +02:00
parent 4be44ecad0
commit 1f6faa2a70
2 changed files with 352 additions and 79 deletions

View File

@ -3,6 +3,7 @@ import os
from datetime import datetime
from logic.match_manager import match_manager
from logic.queue_handler import matchmaking_queue
from flask_definitions import *
import uuid
@ -37,7 +38,18 @@ def queue_info():
region = request.args.get("region")
count_a = request.args.get("countA") # Hunter Count
count_b = request.args.get("countB") # Runner Count
return jsonify({"A": {"Size": 1, "ETA": 100, "stable": True}, "B": {"Size": 5, "ETA": 100, "stable": True}, "SizeA": count_a, "SizeB": count_b})
side = request.args.get("side", "")
session = matchmaking_queue.getSession(userid)
if not side:
return jsonify({"message": "Side parameter is missing"}), 400
if side not in ["A", "B"]:
return jsonify({"message": "Invalid side parameter"}), 400
response_data = matchmaking_queue.getQueueStatus(side, session)
return jsonify(response_data)
# return jsonify({"A": {"Size": 1, "ETA": 100, "stable": True}, "B": {"Size": 5, "ETA": 100, "stable": True}, "SizeA": count_a, "SizeB": count_b})
@app.route("/api/v1/queue", methods=["POST"])
@ -69,52 +81,72 @@ def queue():
logger.graylog_logger(level="info", handler="logging_queue",
message=f"User {userid} is queueing for {category} in {region} with {count_a} hunters and {count_b} runners")
try:
queue_data = side, check_only
if not check_only:
matchmaking_queue.queuePlayer(side=side, userId=userid)
# return {"status":"QUEUED","queueData":{"ETA":-10000,"position":0,"stable":False}}
# else:
# # check here if more than one User is in the Queue and create a Match.
# return {"status":"QUEUED","queueData":{"ETA":-8000,"position":0,"stable":False}}
response_data = {
"queueData": {
"ETA": -10000,
"position": 0,
"sizeA": 1 if side == "A" else 0,
"sizeB": 1 if side == "B" else 0,
"stable": False,
},
"status": "QUEUED",
}
return jsonify(response_data)
# data = match_manager.find_match_id_from_user(user_id)
# if data is None:
# print("No match found")
# # PlaceHolder for If User not in private match
else:
response_data = matchmaking_queue.getQueueStatus(side, userid)
return jsonify(response_data)
tmp = {"status": "MATCHED", "queueData": {"ETA": 0, "position": 0, "stable": False},
"matchData": {"matchId": "asdasdassdasd-ddddd-asdasd-asdas-bfbfbfbfb", "schema": 3,
"category": "oman-100372-dev:None:Windows::us-east-1:1:4:0:G:2", "rank": 1, "geolocation": {},
"creationDateTime": 10101010101, "status": "CREATED",
"creator": "asdasdasdasdasdasdasd", "customData": {}, "version": 1,
"skill": {"longitude": 1.1, "latitude": 1.1, "rank": 20,
"rating": {"rating": 11111, "RD": 111.1111, "volatility": 1.11}, "countries": ["AT"],
"x": 20, "isSuspicious": False, "isProtected": False, "version": 2}, "churn": 0,
"props": {"countA": 1,
"countB": 4, "gameMode": "None", "platform": "Windows", "isDedicated": False},
"reason": "", "sideA": ["asdasdasdasdasdasdasd"],
"sideB": ["dsadasdasdasdasdasddsa"],
"playerHistory": ["asdasdasdasdasdasdasd",
"dsadasdasdasdasdasddsa"]}}
if region == "DEV":
all_users = [userid]
if additional_user_ids:
all_users.append(additional_user_ids)
return jsonify(
{"status": "MATCHED", "QueueData": {"Position": 0, "ETA": 0, "Stable": False, "SizeA": 1, "SizeB": 4},
"MatchData": {"MatchId": spoofed_match_id, "Category": category, "Rank": rank,
"CreationDateTime": epoch, "ExcludeFriends": False,
"ExcludeClanMembers": False, "Status": "CREATED",
"Creator": userid,
"Players": [all_users],
"SideA": [userid],
"SideB": [additional_user_ids], "CustomData": {},
"Props": {"isDedicated": False, "gameMode": "08d2279d2ed3fba559918aaa08a73fa8-Default",
'MatchConfiguration': '/Game/Configuration/MatchConfig/MatchConfig_Demo.MatchConfig_Demo'},
"Schema": 11122334455666}})
else:
return {"status": "QUEUED", "queueData": {"ETA": -10000, "position": 0, "stable": False}}
except Exception as e:
logger.graylog_logger(level="error", handler="queue", message=e)
return jsonify({"message": "Internal Server Error"}), 500
# if region == "DEV":
# all_users = [userid]
# if additional_user_ids:
# all_users.append(additional_user_ids)
# return jsonify(
# {"status": "MATCHED", "QueueData": {"Position": 0, "ETA": 0, "Stable": False, "SizeA": 1, "SizeB": 4},
# "MatchData": {"MatchId": spoofed_match_id, "Category": category, "Rank": rank,
# "CreationDateTime": epoch, "ExcludeFriends": False,
# "ExcludeClanMembers": False, "Status": "CREATED",
# "Creator": userid,
# "Players": [all_users],
# "SideA": [userid],
# "SideB": [additional_user_ids], "CustomData": {},
# "Props": {"isDedicated": False, "gameMode": "08d2279d2ed3fba559918aaa08a73fa8-Default",
# 'MatchConfiguration': '/Game/Configuration/MatchConfig/MatchConfig_Demo.MatchConfig_Demo'},
# "Schema": 11122334455666}})
# else:
# return {"status": "QUEUED", "queueData": {"ETA": -10000, "position": 0, "stable": False}}
# eta, position = match_manager.find_eta_and_position(data["_match_uuid"])
@app.route("/api/v1/queue/cancel", methods=["POST"])
def cancel_queue():
check = check_for_game_client("strict")
if not check:
return jsonify({"message": "Endpoint not found"}), 404
session_cookie = request.cookies.get("bhvrSession")
if not session_cookie:
return jsonify({"message": "Endpoint not found"}), 404
userid = session_manager.get_user_id(session_cookie)
if userid == 401:
return jsonify({"message": "Endpoint not found"}), 404
try:
matchmaking_queue.removePlayerFromQueue(userid)
except Exception as e:
logger.graylog_logger(level="error", handler="queue", message=e)
return jsonify({"message": "Internal Server Error"}), 500
return "", 204
@app.route("/api/v1/match/<matchid>", methods=["GET"])
def match(matchid):
check = check_for_game_client("strict")
@ -126,33 +158,13 @@ def match(matchid):
userid = session_manager.get_user_id(session_cookie)
if userid == 401:
return jsonify({"message": "Endpoint not found"}), 404
if matchid == "0051681e-72ce-46f0-bda2-752e471d0d08":
return jsonify({"matchId": matchid, "schema": 3,
"category": "Steam-te-18f25613-36778-ue4-374f864b",
"geolocation": {}, "creationDateTime": datetime.now().timestamp(),
"status": "OPENED",
"creator": "00658d11-2dfd-41e8-b6d2-2462e8f3aa47",
"customData": {
"SessionSettings": "AAAAJDYxOWQ2ZjQyLWRiODctNGYzZS04ZGM5LTNjOTk5NTYxMzYxNAAAAAAAAAAAAAAAYwAAAGMAAAAAAQAAAAEAAQEAAAAAAAAAAAAKAAAAB01hcE5hbWUGAAAAA21hcAIAAAAQQ1VTVE9NU0VBUkNISU5UMgHRD3M7AgAAAAhnYW1lTW9kZQYAAAAoMDhkMjI3OWQyZWQzZmJhNTU5OTE4YWFhMDhhNzNmYTgtRGVmYXVsdAIAAAAOUHJvamVjdFZlcnNpb24GAAAAHnRlLTE4ZjI1NjEzLTM2Nzc4LXVlNC0zNzRmODY0YgIAAAAQQ1VTVE9NU0VBUkNISU5UMQEAAAABAgAAAAdNYXhSYW5rAQAAAAECAAAAB01pblJhbmsBAAAAAQIAAAAQTWlycm9yc1Nlc3Npb25JZAYAAAAkMDA1MTY4MWUtNzJjZS00NmYwLWJkYTItNzUyZTQ3MWQwZDA4AgAAAAxQbGF0Zm9ybU5hbWUGAAAABVN0ZWFtAgAAABFQbGF0Zm9ybVNlc3Npb25JZAYAAAAsMXx8NzY1NjExOTkxNjk3ODEyODU6Nzc3N3wxMDk3NzUyNDI5MDM4OTYwOTgC"}
, "version": 2, "churn": 0, 'props':
{"gameMode": "08d2279d2ed3fba559918aaa08a73fa8-Default",
'MatchConfiguration': '/Game/Configuration/MatchConfig/MatchConfig_Demo.MatchConfig_Demo'},
"reason": "", "SideA": ["619d6f42-db87-4f3e-8dc9-3c9995613614"],
"SideB": ["00658d11-2dfd-41e8-b6d2-2462e8f3aa47"]})
else:
json_dict = match_manager.find_json_with_match_id(matchid)
return jsonify({"matchId": matchid, "schema": 3,
"category": json_dict["category"],
"geolocation": {}, "creationDateTime": json_dict["time_created"],
"status": "OPENED",
"creator": json_dict["creator"],
"customData": json_dict["custom_data"], "version": 2, "churn": 0,
"props": {"countA": 1, "countB": 4,
"gameMode": "None",
"platform": "Windows",
"isDedicated": False},
"reason": "", "sideA": [json_dict["players_side_a"]],
"sideB": [json_dict["players_side_b"]]})
try:
response_data = matchmaking_queue.createMatchResponse(matchid)
return jsonify(response_data)
except Exception as e:
logger.graylog_logger(level="error", handler="match", message=e)
return jsonify({"message": "Internal Server Error"}), 500
@app.route("/api/v1/match/<matchid>/Kill", methods=["PUT"])
@ -166,8 +178,20 @@ def match_kill(matchid):
userid = session_manager.get_user_id(session_cookie)
if userid == 401:
return jsonify({"message": "Endpoint not found"}), 404
logger.graylog_logger(level="info", handler="match_kill", message=f"Match {matchid} has been killed")
return jsonify({"status": "OK"})
try:
lobby, _ = matchmaking_queue.getLobbyById(matchid)
if lobby and matchmaking_queue.isOwner(matchid, userid):
response_data = matchmaking_queue.createMatchResponse(matchid, killed=True)
matchmaking_queue.deleteMatch(matchid)
return jsonify(response_data)
else:
return jsonify({"message": "Unauthorized"}), 401
except Exception as e:
logger.graylog_logger(level="error", handler="match", message=e)
return jsonify({"message": "Internal Server Error"}), 500
@app.route("/api/v1/match/<match_id>/register", methods=["POST"])
@ -182,16 +206,25 @@ def match_register(match_id):
userid = session_manager.get_user_id(session_cookie)
if userid == 401:
return jsonify({"message": "Endpoint not found"}), 404
custom_data = request.get_json("customData")
if custom_data["sessionSettings"]:
session_settings = custom_data["sessionSettings"]
logger.graylog_logger(level="info", handler="match_register",
message=f"User {userid} is registering to match {match_id}")
return jsonify({"status": "OK"})
data = request.json
custom_data = data.get("customData", {})
session_settings = custom_data.get("SessionSettings", "")
response = matchmaking_queue.registerMatch(match_id, session_settings)
if response:
return jsonify(response)
else:
return jsonify({"message": "Match registration failed"}), 500
except Exception as e:
logger.graylog_logger(level="error", handler="match_register", message=e)
return jsonify({"status": "ERROR"})
return jsonify({"message": "Internal Server Error"}), 500
@app.route("/api/v1/match/<match_id>/Quit", methods=["PUT"])
@ -208,11 +241,24 @@ def match_quit(match_id):
return jsonify({"message": "Endpoint not found"}), 404
logger.graylog_logger(level="info", handler="logging_queue",
message=f"User {userid} is quieting match {match_id}")
return jsonify({"status": "OK"})
message=f"User {userid} is quitting match {match_id}")
lobby, _ = matchmaking_queue.getLobbyById(match_id)
if lobby:
if matchmaking_queue.isOwner(match_id, userid):
matchmaking_queue.deleteMatch(match_id)
else:
matchmaking_queue.removePlayerFromQueue(userid)
return "", 204
else:
return jsonify({"message": "Match not found"}), 404
except Exception as e:
logger.graylog_logger(level="error", handler="logging_queue", message=e)
return jsonify({"status": "ERROR"})
return jsonify({"status": "ERROR"}), 500
@app.route("/api/v1/match/create", methods=["POST"])
@ -239,7 +285,7 @@ def match_create():
players_b = request.json.get("playersB")
props = request.json.get("props")
match_id = match_manager.create_match(request.json)
match_id = matchmaking_queue.create_match(request.json)
epoch = datetime.now().timestamp()
player_list = [players_b, players_a]

227
src/logic/queue_handler.py Normal file
View File

@ -0,0 +1,227 @@
import uuid
from logic.time_handler import get_time
class QueueData:
def __init__(self, side):
self.side = side
class Session:
def __init__(self, userid, clientIds):
self.userid = userid
self.clientIds = clientIds
class QueuedPlayer:
def __init__(self, userId, side, lastCheckedForMatch):
self.userId = userId
self.side = side
self.lastCheckedForMatch = lastCheckedForMatch
class Lobby:
def __init__(self, isReady, host, nonHosts, id, isPrepared, hasStarted):
self.isReady = isReady
self.host = host
self.nonHosts = nonHosts
self.id = id
self.isPrepared = isPrepared
self.hasStarted = hasStarted
class KilledLobby:
def __init__(self, id, reason, killedTime):
self.id = id
self.reason = reason
self.killedTime = killedTime
class MatchmakingQueue:
def __init__(self):
self.openLobbies = []
self.killedLobbies = []
self.queuedPlayers = []
def queuePlayer(self, side, userId):
queuedPlayer = QueuedPlayer(userId, side,
get_time())
self.queuedPlayers.append(queuedPlayer)
def getQueuedPlayer(self, userid):
for i, queuedPlayer in enumerate(self.queuedPlayers):
if queuedPlayer.userId == userid:
return queuedPlayer, i
return None, -1
def getQueueStatus(self, side, userid):
queuedPlayer, index = self.getQueuedPlayer(userid)
if not queuedPlayer:
return {}
if side == 'B':
if self.openLobbies:
for openLobby in self.openLobbies:
if not openLobby.isReady or openLobby.hasStarted:
continue
if len(openLobby.nonHosts) < 4:
openLobby.nonHosts.append(queuedPlayer)
self.queuedPlayers.pop(index)
return self.createQueueResponseMatched(openLobby.host.userId, openLobby.id,
userid)
else:
return {
"queueData": {
"ETA": -10000,
"position": 0,
"sizeA": 0,
"sizeB": 1,
},
"status": "QUEUED",
}
else:
matchId = self.genMatchUUID()
lobby = Lobby(False, queuedPlayer, [], matchId, False, False)
self.openLobbies.append(lobby)
return self.createQueueResponseMatched(userid, matchId)
def removePlayerFromQueue(self, userid):
_, index = self.getQueuedPlayer(userid)
if index != -1:
self.queuedPlayers.pop(index)
def getLobbyById(self, id):
for i, lobby in enumerate(self.openLobbies):
if lobby.id == id:
return lobby, i
return None, -1
def getKilledLobbyById(self, id):
for killedLobby in self.killedLobbies:
if killedLobby.id == id:
return killedLobby
return None
def registerMatch(self, matchId, sessionSettings):
lobby, _ = self.getLobbyById(matchId)
if lobby:
lobby.isReady = True
lobby.sessionSettings = sessionSettings
return self.createMatchResponse(matchId)
def deleteMatch(self, matchId):
lobby, index = self.getLobbyById(matchId)
if lobby:
self.openLobbies.pop(index)
killedLobby = KilledLobby(lobby.id, lobby.reason, get_time())
self.killedLobbies.append(killedLobby)
def isOwner(self, matchId, userid):
lobby, _ = self.getLobbyById(matchId)
return lobby and lobby.host.userid == userid
def deleteOldMatches(self):
current_time = get_time()
for i in range(len(self.killedLobbies) - 1, -1, -1):
if self.killedLobbies[i].killedTime < current_time - 5 * 60 * 1000:
self.killedLobbies.pop(i)
def createMatchResponse(self, matchId, killed=False):
lobby = self.getLobbyById(matchId)[0] or self.getKilledLobbyById(matchId)
if not lobby:
return {}
return {
"category": "oman-100372-dev:None:Windows:::1:4:0:G:2",
"churn": 0,
"creationDateTime": get_time(),
"creator": lobby.host.userId,
"customData": {
"SessionSettings": lobby.sessionSettings or "",
},
"geolocation": {},
"matchId": matchId,
"props": {
"countA": 1,
"countB": 4,
"EncryptionKey": "Rpqy9fgpIWrHxjJpiwnJJtoZ2hbUZZ4paU+0n4K/iZI=",
"gameMode": "None",
"platform": "Windows",
},
"rank": 1,
"reason": lobby.reason or "",
"schema": 3,
"sideA": [lobby.host.userId],
"sideB": [player.userId for player in lobby.nonHosts],
"skill": {
"continent": "NA",
"country": "US",
"latitude": 0,
"longitude": 0,
"rank": 20,
"rating": {
"rating": 1500,
"RD": 347.4356,
"volatility": 0.06,
},
"regions": {
"good": ["us-east-1"],
"ok": ["us-east-1"],
},
"version": 2,
"x": 20,
},
"status": "KILLED" if killed else "OPENED",
"version": 2,
}
def createQueueResponseMatched(self, creatorId, matchId, joinerId=None):
return {
"status": "MATCHED",
"matchData": {
"category": "oman-100372-dev:None:Windows:::1:4:0:G:2",
"churn": 0,
"creationDateTime": get_time(),
"creator": creatorId,
"customData": {},
"geolocation": {},
"matchId": matchId,
"props": {
"countA": 1,
"countB": 4,
"EncryptionKey": "Rpqy9fgpIWrHxjJpiwnJJtoZ2hbUZZ4paU+0n4K/iZI=",
"gameMode": "None",
"platform": "Windows",
},
"rank": 1,
"reason": "",
"schema": 3,
"sideA": [creatorId],
"sideB": [joinerId] if joinerId else [],
"skill": {
"continent": "NA",
"country": "US",
"latitude": 0,
"longitude": 0,
"rank": 20,
"rating": {
"rating": 1500,
"RD": 347.4356,
"volatility": 0.06,
},
"regions": {
"good": ["us-east-1"],
"ok": ["us-east-1"],
},
"version": 2,
"x": 20,
},
"status": "CREATED",
"version": 1,
},
}
def genMatchUUID(self):
return str(uuid.uuid4())
matchmaking_queue = MatchmakingQueue()