DeathGarden_API_Rebirth/src/endpoints/matchmaking.py

1185 lines
44 KiB
Python

import json
import os
from datetime import datetime
from logic.time_handler import get_time
from logic.queue_handler import matchmaking_queue
from flask_definitions import *
@app.route("/api/v1/config/MATCH_MAKING_REGIONS/raw", methods=["GET"])
def match_making_regions_raw():
check_for_game_client("strict")
try:
return jsonify(["EU"])
except TimeoutError:
return jsonify({"status": "error"})
except Exception as e:
logger.graylog_logger(level="error", handler="matchmaking_RegionsRAW", message=e)
@app.route("/api/v1/queue/info", methods=["GET"])
def queue_info():
try:
# ?category=Steam-te-23ebf96c-27498-ue4-7172a3f5&gameMode=Default&region=US&countA=1&countB=5
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
category = sanitize_input(request.args.get("category"))
game_mode = sanitize_input(request.args.get("gameMode"))
region = sanitize_input(request.args.get("region"))
count_a = request.args.get("countA") # Hunter Count
count_b = request.args.get("countB") # Runner Count
side = sanitize_input(request.args.get("side", ""))
session = matchmaking_queue.getSession(userid)
if not side:
return jsonify({"A": {"Size": 1, "ETA": 100, "stable": True}, "B": {"Size": 5, "ETA": 100, "stable": True},
"SizeA": count_a, "SizeB": count_b})
if side not in ["A", "B"]:
return jsonify({"message": "Invalid side parameter"}), 400
response_data = matchmaking_queue.getQueueStatus(side, session, region)
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})
except TimeoutError:
return jsonify({"status": "TimeoutError"})
except Exception as e:
logger.graylog_logger(level="error", handler="queue_info", message=e)
@app.route("/api/v1/queue", methods=["POST"])
def queue():
# {"category":"Steam-te-18f25613-36778-ue4-374f864b","rank":1,"side":"B","latencies":[],"additionalUserIds":[],
# "checkOnly":false,"gameMode":"08d2279d2ed3fba559918aaa08a73fa8-Default","region":"US","countA":1,"countB":5}
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
category = sanitize_input(request.json.get("category"))
rank = request.json.get("rank")
side = sanitize_input(request.json.get("side"))
latencies = sanitize_input("latencies")
additional_user_ids = request.json.get("additionalUserIds")
check_only = request.json.get("checkOnly") # False = Searching. True = Is searching and waiting for match
game_mode = sanitize_input(request.json.get("gameMode"))
region = sanitize_input(request.json.get("region"))
count_a = request.json.get("countA")
count_b = request.json.get("countB")
epoch = datetime.now().timestamp()
if additional_user_ids:
logger.graylog_logger(level="info",
handler="logging_queue",
message=f"User {userid} is queueing for {category} in "
f"{region} with {count_a} hunters and {count_b} "
f"runners and these additional Users {additional_user_ids}")
else:
logger.graylog_logger(level="info", handler="logging_queue",
message=f"User {userid} is queueing for {category} in {region} with "
f"{count_a} hunters and {count_b} runners")
try:
if not check_only:
matchmaking_queue.queuePlayer(side=side, userId=userid)
if additional_user_ids:
for user in additional_user_ids:
matchmaking_queue.queuePlayer(side=side, userId=user)
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)
else:
response_data = matchmaking_queue.getQueueStatus(side, userid, region)
return jsonify(response_data)
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_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
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_unsanitized>", methods=["GET"])
def match(matchid_unsanitized):
matchid = sanitize_input(matchid_unsanitized)
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
try:
response_data = matchmaking_queue.createMatchResponse(matchId=matchid, userId=userid)
# logger.graylog_logger(level="debug", handler="match", message=response_data)
if response_data == "null" or response_data is None:
logger.graylog_logger(level="error", handler="match", message=f"MatchResponse is null for MatchID: {matchid}")
return {
"Category": "Steam-te-18f25613-36778-ue4-374f864b",
"creationDateTime": 0,
"creator": None,
"customData": {
},
"MatchId": matchid,
"props": {
"countA": 0,
"countB": 0,
"gameMode": None,
"MatchConfiguration": None,
"platform": "Windows",
},
"rank": 1,
"Schema": 3,
"sideA": [],
"sideB": [],
"Players": [],
"status": "Destroyed"
}
# response_data = matchmaking_queue.getKilledLobbyById(matchid)
# logger.graylog_logger(level="debug", handler="match", message=response_data)
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_unsanitized>/Kill", methods=["PUT"])
def match_kill(matchid_unsanitized):
matchid = sanitize_input(matchid_unsanitized)
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
try:
lobby, _ = matchmaking_queue.getLobbyById(matchid)
if lobby and matchmaking_queue.isOwner(matchid, userid):
response_data = matchmaking_queue.createMatchResponse(matchId=matchid, killed=True)
matchmaking_queue.deleteMatch(matchid)
logger.graylog_logger(level="info", handler="match_kill", message=f"Killed Match: {matchid}"
f"by User: {userid}")
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_unsanitized>/register", methods=["POST"])
def match_register(match_id_unsanitized):
try:
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
match_id = sanitize_input(match_id_unsanitized)
logger.graylog_logger(level="info", handler="match_register",
message=f"User {userid} is registering to match {match_id}")
data = request.json
custom_data = data.get("customData", {})
session_settings = custom_data.get("SessionSettings", "")
response = matchmaking_queue.registerMatch(match_id, session_settings, userid)
if response:
if use_discord:
game_mode = response["props"]["gameMode"]
if game_mode == "789c81dfb11fe39b7247c7e488e5b0d4-Default":
game_mode = "Default"
elif game_mode == "08d2279d2ed3fba559918aaa08a73fa8-Default":
game_mode = "Default"
elif game_mode == "d071fb6678668246d6d999c9892c2a60-Default":
game_mode = "Default"
elif game_mode == "e3744bb4acbc0e5dc107e999c6132f18-Default":
game_mode = "REMIX"
elif game_mode == "a0fdf9ce92261dcc5492f353b62f34f3-Default":
game_mode = "4 Needles"
else:
game_mode = "Default"
match_configuration = response["props"]["MatchConfiguration"]
if match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_Demo_HarvestYourExit_1v5.MatchConfig_Demo_HarvestYourExit_1v5":
match_configuration = "Harvest Your Exit 1v5"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_Demo.MatchConfig_Demo":
match_configuration = "Survival"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_ARC_Fortress.MatchConfig_ARC_Fortress":
match_configuration = "Arctic Fortress"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_CustomMatch.MatchConfig_CustomMatch":
match_configuration = "REMIX Deathgarden - Gold Rush"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_Demo_2v8_4Needles.MatchConfig_Demo_2v8_4Needles":
match_configuration = "Harvest Your Exit 4Needles"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_DES_City_2Hunters.MatchConfig_DES_City_2Hunters":
match_configuration = "Desert City 5 needles"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_WA_Rivers.MatchConfig_WA_Rivers":
match_configuration = "Creek"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_WA_Cemetery.MatchConfig_WA_Cemetery":
match_configuration = "Tombstone"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_DES_Oilfield.MatchConfig_DES_Oilfield":
match_configuration = "Blowout"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_ARC_BlastFurnace.MatchConfig_ARC_BlastFurnace":
match_configuration = "Fire in the Sky"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_DES_Mayan.MatchConfig_DES_Mayan":
match_configuration = "Dust & Blood"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_ARC_Expedition.MatchConfig_ARC_Expedition":
match_configuration = "Arctic Expedition"
elif match_configuration == "/Game/Configuration/MatchConfig/MatchConfig_RUI_All.MatchConfig_RUI_All":
match_configuration = "First Strike"
if dev_env == "false":
webhook_data = {
"content": "",
"embeds": [
{
"title": f"Match Registered",
"description": f"MatchID: {match_id} \n GameMode: {game_mode} \n MatchConfiguration: {match_configuration}",
"color": 7932020
}
],
"attachments": []
}
try:
discord_webhook(url_list, webhook_data)
except Exception as e:
logger.graylog_logger(level="error", handler="discord_webhook_message", message=e)
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({"message": "Internal Server Error"}), 500
@app.route("/api/v1/match/<match_id_unsanitized>/Quit", methods=["PUT"])
def match_quit(match_id_unsanitized):
try:
# todo If OWNER Crash -> Runner sends QUIT acknowledge when players less then 2
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
match_id = sanitize_input(match_id_unsanitized)
logger.graylog_logger(level="info", handler="logging_queue",
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)
matchmaking_queue.remove_player_from_match(match_id, 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"}), 500
@app.route("/api/v1/match/<match_id_unsanitized>/Close", methods=["PUT"])
def close_lobby(match_id_unsanitized):
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
match_id = sanitize_input(match_id_unsanitized)
try:
lobby, _ = matchmaking_queue.getLobbyById(match_id)
if lobby:
if matchmaking_queue.isOwner(match_id, userid):
#matchmaking_queue.deleteMatch(match_id)
#return "", 204
# set lobby.status to "Closed"
matchmaking_queue.closeMatch(match_id)
data = matchmaking_queue.createMatchResponse(matchId=match_id, killed=False, userId=userid)
return jsonify(data), 200
else:
return jsonify({"message": "Unauthorized"}), 401
else:
return jsonify({"message": "Match not found"}), 404
except Exception as e:
logger.graylog_logger(level="error", handler="close_lobby", message=e)
return jsonify({"message": "Internal Server Error"}), 500
@app.route("/api/v1/match/create", methods=["POST"])
def match_create():
# {'category': 'Steam-te-18f25613-36778-ue4-374f864b', 'region': 'US', 'playersA': [],
# 'playersB': ['00658d11-2dfd-41e8-b6d2-2462e8f3aa47', '95041085-e7e4-4759-be3d-e72c69167578',
# '0385496c-f0ae-44d3-a777-26092750f39c'],
# 'props': {'MatchConfiguration': '/Game/Configuration/MatchConfig/MatchConfig_Demo.MatchConfig_Demo'},
# 'latencies': []}
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
category = sanitize_input(request.json.get("category"))
region = sanitize_input(request.json.get("region"))
players_a = request.json.get("playersA")
players_b = request.json.get("playersB")
props = request.json.get("props")["MatchConfiguration"]
# match_send = matchmaking_queue.createQueueResponseMatched(userid, matchid, joinerId=players_a)
epoch = datetime.now().timestamp()
player_list = []
for player in players_a:
player_list.append(player)
for player in players_b:
player_list.append(player)
matchid, Match_Config = matchmaking_queue.register_private_match(players_a=players_a, players_b=players_b, match_config=props)
# EPrivateMatchState:
# Disabled
# InGameSession
# NotCreated
# NotEnoughPlayers
# HunterNotSelected
# CanStart
# EMatchmakingState:
# None
# SearchingForMatch
# WaitingForPlayers
data = {"MatchId": matchid, "Category": category, "Rank": 1, "Region": region,
"CreationDateTime": epoch, "ExcludeFriends": False,
"ExcludeClanMembers": False, "Status": "OPEN", "Reason": "FString", "Creator": userid,
"Players": player_list, "SideA": players_a, "SideB": players_b, "CustomData": {}, "Props": props,
"Schema": 1}
users = players_a.copy()
users.append(players_b)
current_time = get_time()[0]
countB = len(players_b)
data = {
"status": "PendingWarparty",
"QueueData": {
"Position": 1,
"ETA": 0,
"Stable": False,
"SizeA": 1,
"SizeB": countB
},
"matchData": {
"category": "Steam-te-18f25613-36778-ue4-374f864b",
"creationDateTime": current_time,
"creator": players_a,
"customData": {},
"matchId": matchid,
"props": {
"countA": 1,
"countB": countB,
"gameMode": Match_Config["gameMode"],
"MatchConfiguration": Match_Config["MatchConfiguration"],
"platform": "Windows",
},
"rank": 1,
"schema": 3,
"sideA": players_a,
"sideB": players_b,
"Players": users,
"status": "CREATED"
}
}
return jsonify(data)
@app.route("/api/v1/extensions/progression/playerEndOfMatch", methods=["POST"])
def progression_player_end_of_match():
# {
# "data":{
# "playerId":"00658d11-2dfd-41e8-b6d2-2462e8f3aa47",
# "faction":"Runner",
# "characterGroup":"RunnerGroupE",
# "playtime":547,
# "platform":"PC",
# "hasQuit":false,
# "characterState":"Escaped",
# "matchId":"A9B72FEC485695753C8181AE3168877B-1",
# "experienceEvents":[
# {
# "type":"MarkedSupplier",
# "amount":600
# },
# {
# "type":"ResourceLootBonus",
# "amount":510
# },
# {
# "type":"PlayTime",
# "amount":45
# },
# {
# "type":"FriendlyEffectBonus",
# "amount":975
# },
# {
# "type":"ResourceDepositBonus",
# "amount":3750
# },
# {
# "type":"GoldenCrateLootBonus",
# "amount":5
# },
# {
# "type":"EscapeBonus",
# "amount":1000
# },
# {
# "type":"ResourceEscapeBonus",
# "amount":20
# },
# {
# "type":"BloodModeEscapeBonus",
# "amount":100
# }
# ],
# "earnedCurrencies":[
# {
# "currencyName":"CurrencyA",
# "amount":617
# },
# {
# "currencyName":"CurrencyB",
# "amount":620
# },
# {
# "currencyName":"CurrencyC",
# "amount":619
# }
# ],
# "completedChallenges":[
# {
# "challengeId":"24CE65364362CB2A90C0E08876176937",
# "challengeType":"Challenge.Type.Progression"
# },
# {
# "challengeId":"B4B156CC47C8D987B9BDBEB910B12C9E",
# "challengeType":"Challenge.Type.Progression"
# },
# {
# "challengeId":"ECCBA78D4055676F9C17D79B9D5FA2D4",
# "challengeType":"Challenge.Type.Progression"
# }
# ]
# }
# }
#
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
# userid = session_manager.get_user_id(session_cookie)
try:
logger.graylog_logger(level="info", handler="matchmaking_playerEndOfMatch", message=request.get_json())
data = request.get_json()["data"]
userid = data["playerId"]
characterGroup = data["characterGroup"]
experience = 0
for event in data["experienceEvents"]:
experience += event["amount"]
update_user_xp(userid, experience, characterGroup)
user_wallet = mongo.get_data_with_list(login=userid, login_steam=False, items={"currency_iron",
"currency_blood_cells",
"currency_ink_cells"})
CurrencyA = user_wallet["currency_iron"]
CurrencyB = user_wallet["currency_blood_cells"]
CurrencyC = user_wallet["currency_ink_cells"]
wallet = []
for currency in data["earnedCurrencies"]:
if currency["currencyName"] == "CurrencyA":
wallet.append({"Currency": "CurrencyA", "Amount": currency["amount"]})
CurrencyA += currency["amount"]
elif currency["currencyName"] == "CurrencyB":
wallet.append({"Currency": "CurrencyB", "Amount": currency["amount"]})
CurrencyB += currency["amount"]
elif currency["currencyName"] == "CurrencyC":
wallet.append({"Currency": "CurrencyC", "Amount": currency["amount"]})
CurrencyC += currency["amount"]
mongo.write_data_with_list(login=userid, login_steam=False, items_dict={"currency_iron": CurrencyA,
"currency_blood_cells": CurrencyB,
"currency_ink_cells": CurrencyC})
# return jsonify({
# "Player": {
# "InitialExperienceProgressions": [
# FGMProgressionExperienceModel
# ],
# "NewExperienceProgressions": [
# FGMProgressionExperienceModel
# ],
# "TotalEarnedCurrency": [
# FCurrencyModel
# ],
# "Wallet": [
# FCurrencyModel
# ],
# "Events": [
# FGMExperienceEvent
# ]
# }
# })
# MirrorsExtModelProgressionPlayerEndOfMatchResponse
# todo remove this test
return jsonify({"Player": {"Success": True}})
return jsonify({
"Player": {
"InitialExperienceProgressions": [
{
"ObjectId": characterGroup,
"Level": 1,
"ExperienceToReach": 100000,
"CurrentExperience": 1
}
],
"NewExperienceProgressions": [
{
"ObjectId": characterGroup,
"Level": 1,
"ExperienceToReach": 100000,
"CurrentExperience": experience
}
],
"TotalEarnedCurrency": wallet,
"Wallet": wallet,
"Events": [
]
}
})
except TimeoutError:
return jsonify({"status": "error"})
except Exception as e:
logger.graylog_logger(level="error", handler="matchmaking_playerEndOfMatch", message=e)
@app.route("/api/v1/extensions/progression/endOfMatch", methods=["POST"])
def progression_end_of_match():
# todo Implement this fully
# {
# "data":{
# "players":[
# {
# "playerId":"619d6f42-db87-4f3e-8dc9-3c9995613614",
# "faction":"Runner",
# "characterGroup":"RunnerGroupE",
# "platform":"PC",
# "hasQuit":false,
# "characterState":"Dead"
# },
# {
# "playerId":"95041085-e7e4-4759-be3d-e72c69167578",
# "faction":"Hunter",
# "characterGroup":"HunterGroupB",
# "platform":"PC",
# "hasQuit":false,
# "characterState":"InArena"
# },
# {
# "playerId":"00658d11-2dfd-41e8-b6d2-2462e8f3aa47",
# "faction":"Runner",
# "characterGroup":"RunnerGroupB",
# "platform":"PC",
# "hasQuit":false,
# "characterState":"Dead"
# }
# ],
# "dominantFaction":"Hunter",
# "matchId":"df9655d9-63ac-4dde-a9e9-129aaa249356"
# }
# }
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
try:
players = []
for player in request.get_json()["data"]["players"]:
player = player["playerId"]
response = {
"playerId": player,
# What could this struct be????
}
logger.graylog_logger(level="info", handler="matchmaking_endOfMatch", message=request.get_json())
matchId = request.get_json()["data"]["matchId"]
matchmaking_queue.deleteMatch(matchId)
return jsonify({"Success": True})
except TimeoutError:
return jsonify({"Success": False})
except Exception as e:
logger.graylog_logger(level="error", handler="matchmaking_endOfMatch", message=e)
return jsonify({"Success": False})
@app.route("/metrics/endofmatch/event", methods=["POST"])
def metrics_end_of_match_event():
#
# {
# "matchId": "A9B72FEC485695753C8181AE3168877B-1",
# "gameVersion": "te-f9b4768a-26590-ue4-cefc1aee",
# "roundEvents": [
# {
# "round": 1,
# "roundLength": 547,
# "gamemode": "None",
# "biomeName": "",
# "randomSeed": 0,
# "runnerCount": 2,
# "hunterCount": 0,
# "weather": "",
# "supplierCounts": [],
# "endOfRoundObjectiveState":
# {
# "bAreObjectivesCompleted": false,
# "fullyCapturedObjectivesCount": 0,
# "objectivesFilledCompartmentsCount": 0,
# "progressionByObjective": []
# },
# "endOfRoundExitState":
# {
# "bAreExitOpened": true,
# "openTime": 548.63604736328125,
# "openReason": "HunterDomination",
# "exitCoords": [
# {
# "x": -4371.796875,
# "y": -7400,
# "z": -400
# },
# {
# "x": 3942.046875,
# "y": 7000,
# "z": -400
# }
# ]
# }
# }
# ],
# "runnerMatchAnalytics": [
# {
# "roundEvents": [
# {
# "caltrops": [],
# "specialArrows": [],
# "role": "PlayerRole.Support",
# "state": "Escaped",
# "suicideCount": 0,
# "timeOfDeath": -1,
# "rescueCount": 0,
# "rescuedCount": 0,
# "downedCount": 0,
# "damageDealtToHunter": 0,
# "damageDealtToMines": 0,
# "damageDealtToTurrets": 0,
# "damageDealtToDrones": 44,
# "arenaObjectivesMarked": 0,
# "resourceSuppliersMarked": 1,
# "totalObjectsMarked": 1,
# "murderPostRescueCount": 0,
# "rescuedFromMurderPostCount": 0,
# "sentToMurderPostCount": 0,
# "hunterHitsCount": 0,
# "unsecuredBloodCount": 0,
# "bankedBloodCount": 0,
# "round": 1,
# "gamemode": "",
# "country": "DE",
# "gender": "Male",
# "damageTaken": 0,
# "headshotCount": 0,
# "longestHeadshotDistance": -1,
# "totalShotsFired": 3,
# "supplierUsedCounts": [
# {
# "type": "SupplyCrate_AmmunitionPack_C",
# "count": 3
# },
# {
# "type": "SupplyCrate_WeaponParts_C",
# "count": 1
# },
# {
# "type": "BP_ResourceCrate_ResourceTypeB_C",
# "count": 66
# },
# {
# "type": "GoldenCrate_Unlimited_C",
# "count": 1
# }
# ],
# "resourcesUsage": [
# {
# "type": "Resource.Ammo.ReserveEarned",
# "amount": 34
# },
# {
# "type": "Resource.NPIEarned",
# "amount": 3
# },
# {
# "type": "Resource.NPIExpended",
# "amount": 3
# },
# {
# "type": "Resource.ResourceCharge.TypeB.UnsecuredEarned",
# "amount": 66
# },
# {
# "type": "Resource.ResourceCharge.TypeB.UnsecuredExpended",
# "amount": 56
# },
# {
# "type": "Resource.ResourceCharge.TypeB.BankedEarned",
# "amount": 56
# },
# {
# "type": "Resource.Ammo.ClipExpended",
# "amount": 2
# }
# ],
# "pingInformation":
# {
# "readCount": 108,
# "average": 0,
# "variance": 0,
# "maximum": 0,
# "minimum": 0
# },
# "crouchingTime": 18.294017791748047,
# "capturingObjectivesTime": 0,
# "holdingKeyTime": 0,
# "keysDelivered": 0,
# "objectsDestroyedCount": 1,
# "perksChosen": [
# "Shimmey",
# "Ammo Opportunist"
# ],
# "chatMessagesCounts": [],
# "effectsAppliedCounts": [],
# "customizationItems": [
# {
# "itemSlot": "Head",
# "customizationItem": "Default Smoke Head"
# },
# {
# "itemSlot": "Outfit",
# "customizationItem": "Default Smoke Upperbody"
# },
# {
# "itemSlot": "Lower Body Section",
# "customizationItem": "Default Smoke Lowerbody"
# },
# {
# "itemSlot": "Gauntlet",
# "customizationItem": "Default Smoke Vambrace"
# }
# ],
# "customizationWeaponSkins": [],
# "chaseAmount": 0,
# "chaseAmountEscape": 0
# }
# ],
# "playerName": "HEX: Dino Nuggies",
# "playerUniqueId": "619d6f42-db87-4f3e-8dc9-3c9995613614",
# "playerSessionId": "71E8AFB54DFFCC83A69D3F80A5CBADD5"
# },
# {
# "roundEvents": [
# {
# "caltrops": [],
# "specialArrows": [],
# "role": "PlayerRole.Support",
# "state": "Escaped",
# "suicideCount": 0,
# "timeOfDeath": -1,
# "rescueCount": 0,
# "rescuedCount": 0,
# "downedCount": 0,
# "damageDealtToHunter": 0,
# "damageDealtToMines": 0,
# "damageDealtToTurrets": 0,
# "damageDealtToDrones": 0,
# "arenaObjectivesMarked": 5,
# "resourceSuppliersMarked": 26,
# "totalObjectsMarked": 31,
# "murderPostRescueCount": 0,
# "rescuedFromMurderPostCount": 0,
# "sentToMurderPostCount": 0,
# "hunterHitsCount": 0,
# "unsecuredBloodCount": 0,
# "bankedBloodCount": 0,
# "round": 1,
# "gamemode": "",
# "country": "DE",
# "gender": "Male",
# "damageTaken": 0,
# "headshotCount": 0,
# "longestHeadshotDistance": -1,
# "totalShotsFired": 76,
# "supplierUsedCounts": [
# {
# "type": "SupplyCrate_AmmunitionPack_C",
# "count": 3
# },
# {
# "type": "SupplyCrate_WeaponParts_C",
# "count": 2
# },
# {
# "type": "BP_ResourceCrate_ResourceTypeB_C",
# "count": 51
# },
# {
# "type": "GoldenCrate_Unlimited_C",
# "count": 1
# }
# ],
# "resourcesUsage": [
# {
# "type": "Resource.Ammo.ReserveEarned",
# "amount": 34
# },
# {
# "type": "Resource.Ammo.ClipExpended",
# "amount": 35
# },
# {
# "type": "Resource.NPIEarned",
# "amount": 6
# },
# {
# "type": "Resource.NPIExpended",
# "amount": 6
# },
# {
# "type": "Resource.ResourceCharge.TypeB.UnsecuredEarned",
# "amount": 51
# },
# {
# "type": "Resource.ResourceCharge.TypeB.UnsecuredExpended",
# "amount": 50
# },
# {
# "type": "Resource.ResourceCharge.TypeB.BankedEarned",
# "amount": 50
# }
# ],
# "pingInformation":
# {
# "readCount": 110,
# "average": 225,
# "variance": 82.481819152832031,
# "maximum": 268,
# "minimum": 215
# },
# "crouchingTime": 13.677490234375,
# "capturingObjectivesTime": 0,
# "holdingKeyTime": 0,
# "keysDelivered": 0,
# "objectsDestroyedCount": 0,
# "perksChosen": [
# "Shimmey",
# "Ammo Opportunist"
# ],
# "chatMessagesCounts": [
# {
# "messageType": "IntelliCommsMsgNoChannel",
# "messageCount": 3
# },
# {
# "messageType": "IntelliCommsMsgFaction",
# "messageCount": 23
# }
# ],
# "effectsAppliedCounts": [
# {
# "effectSource": "BP_Arrow_SmokeScreen_C",
# "count": 39
# }
# ],
# "customizationItems": [
# {
# "itemSlot": "Head",
# "customizationItem": "Default Smoke Head"
# },
# {
# "itemSlot": "Outfit",
# "customizationItem": "Default Smoke Upperbody"
# },
# {
# "itemSlot": "Lower Body Section",
# "customizationItem": "Default Smoke Lowerbody"
# },
# {
# "itemSlot": "Gauntlet",
# "customizationItem": "Default Smoke Vambrace"
# }
# ],
# "customizationWeaponSkins": [],
# "chaseAmount": 0,
# "chaseAmountEscape": 0
# }
# ],
# "playerName": "Miraak",
# "playerUniqueId": "00658d11-2dfd-41e8-b6d2-2462e8f3aa47",
# "playerSessionId": "B9BB33074498771555A3DBAF95D124CE"
# },
# {
# "roundEvents": [
# {
# "caltrops": [],
# "specialArrows": [],
# "role": "",
# "state": "Alive",
# "suicideCount": 0,
# "timeOfDeath": -1,
# "rescueCount": 0,
# "rescuedCount": 0,
# "downedCount": 0,
# "damageDealtToHunter": 0,
# "damageDealtToMines": 0,
# "damageDealtToTurrets": 0,
# "damageDealtToDrones": 0,
# "arenaObjectivesMarked": 0,
# "resourceSuppliersMarked": 0,
# "totalObjectsMarked": 0,
# "murderPostRescueCount": 0,
# "rescuedFromMurderPostCount": 0,
# "sentToMurderPostCount": 0,
# "hunterHitsCount": 0,
# "unsecuredBloodCount": 0,
# "bankedBloodCount": 0,
# "round": 1,
# "gamemode": "",
# "country": "",
# "gender": "",
# "damageTaken": 0,
# "headshotCount": 0,
# "longestHeadshotDistance": -1,
# "totalShotsFired": 0,
# "supplierUsedCounts": [],
# "resourcesUsage": [],
# "pingInformation":
# {
# "readCount": 1,
# "average": 0,
# "variance": 0,
# "maximum": 0,
# "minimum": 0
# },
# "crouchingTime": 0,
# "capturingObjectivesTime": 0,
# "holdingKeyTime": 0,
# "keysDelivered": 0,
# "objectsDestroyedCount": 0,
# "perksChosen": [],
# "chatMessagesCounts": [],
# "effectsAppliedCounts": [],
# "customizationItems": [],
# "customizationWeaponSkins": [],
# "chaseAmount": 0,
# "chaseAmountEscape": 0
# }
# ],
# "playerName": "",
# "playerUniqueId": "",
# "playerSessionId": ""
# }
# ],
# "hunterMatchAnalytics": [],
# "hitDetectionErrorRate": 0,
# "hitDetectionErrors": [],
# "lobbyAnalytics": [],
# "eventType": "Server"
# }
#
check_for_game_client("strict")
try:
data = request.get_json()
# Keys LV 1
match_id = data["matchId"]
game_version = data["gameVersion"]
round_events = data["roundEvents"]
runner_match_analytics = data["runnerMatchAnalytics"]
hunter_match_analytics = data["hunterMatchAnalytics"]
hit_detection_error_rate = data["hitDetectionErrorRate"]
hit_detection_errors = data["hitDetectionErrors"]
lobby_analytics = data["lobbyAnalytics"]
event_type = data["eventType"]
# Keys round_events
round_events_item1 = round_events[0]
round_value = round_events_item1["round"]
round_length = round_events_item1["roundLength"]
# gamemode = round_events_item1["gamemode"]
biome_name = round_events_item1["biomeName"]
random_seed = round_events_item1["randomSeed"]
runner_count = round_events_item1["runnerCount"]
hunter_count = round_events_item1["hunterCount"]
weather = round_events_item1["weather"]
supplier_counts = round_events_item1["supplierCounts"]
end_of_round_objective_state = round_events_item1["endOfRoundObjectiveState"]
end_of_round_exit_state = round_events_item1["endOfRoundExitState"]
# Keys end_of_round_objective_state
b_are_objectives_completed = end_of_round_objective_state["bAreObjectivesCompleted"]
fully_captured_objectives_count = end_of_round_objective_state["fullyCapturedObjectivesCount"]
objectives_filled_compartments_count = end_of_round_objective_state["objectivesFilledCompartmentsCount"]
progression_by_objective = end_of_round_objective_state["progressionByObjective"]
# Keys end_of_round_exit_state
b_are_exit_opened = end_of_round_exit_state["bAreExitOpened"]
open_time = end_of_round_exit_state["openTime"]
open_reason = end_of_round_exit_state["openReason"]
exit_coords = end_of_round_exit_state["exitCoords"]
# keys exit_coords
# Per Player XYZ
# Keys runner_match_analytics
# For all players in game LOOP Func
roundEvents = runner_match_analytics[0]["roundEvents"]
playerName = runner_match_analytics[0]["playerName"]
playerUniqueId = runner_match_analytics[0]["playerUniqueId"]
playerSessionId = runner_match_analytics[0]["playerSessionId"]
# Keys hunter_match_analytics
# Keys hit_detection_errors
# Keys lobby_analytics
logger.graylog_logger(level="info", handler="logging_endOfMatch_Event", message=data)
return jsonify({"Success": True})
except TimeoutError:
return jsonify({"Success": False})
except Exception as e:
logger.graylog_logger(level="error", handler="logging_endOfMatch_Event", message=e)
@app.route("/file/<game_version_unsanitized>/<seed_unsanitized>/<map_name_unsanitized>", methods=["POST", "GET"])
def file_gold_rush(seed_unsanitized, map_name_unsanitized, game_version_unsanitized):
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
game_version = sanitize_input(game_version_unsanitized)
seed = sanitize_input(seed_unsanitized)
map_name = sanitize_input(map_name_unsanitized)
userid = session_manager.get_user_id(session_cookie)
file_name = f"{game_version}_{seed}_{map_name}.raw"
folder_path = os.path.join(app.root_path, "map_seeds")
file_path = os.path.join(folder_path, file_name)
os.makedirs(folder_path, exist_ok=True)
if request.method == "GET":
if os.path.isfile(file_path):
with open(file_path, "rb") as files:
data = files.read()
return data
if request.method == "POST":
data = request.get_data()
with open(file_path, "wb") as files:
files.write(data)
return {"status": "success"}
@app.route("/metrics/matchmaking/event", methods=["POST"])
def metrics_matchmaking_event():
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
try:
logger.graylog_logger(level="info", handler="logging_matchmaking_Event", message=request.get_json())
# {
# "playerId": "619d6f42-db87-4f3e-8dc9-3c9995613614",
# "matchId": "",
# "playerRole": "Leader",
# "faction": "Runner",
# "endState": "Cancelled",
# "group": "Default",
# "region": "EU",
# "groupSize": 1,
# "beginTime": 1707419672,
# "endTime": 1707419822,
# "eventType": "Client"
# }
endState = request.get_json()["endState"]
if endState == "Cancelled":
matchmaking_queue.removePlayerFromQueue(userid)
return jsonify({"status": "success"})
return jsonify({"status": "success"})
except TimeoutError:
return jsonify({"status": "error"})
except Exception as e:
logger.graylog_logger(level="error", handler="logging_matchmaking_Event", message=e)
@app.route("/api/v1/match/<match_id_unsanitized>/user/<user_id>", methods=["DELETE"])
def remove_player_from_match(match_id_unsanitized, user_id):
check_for_game_client("strict")
session_cookie = sanitize_input(request.cookies.get("bhvrSession"))
userid = session_manager.get_user_id(session_cookie)
match_id = sanitize_input(match_id_unsanitized)
try:
response = matchmaking_queue.remove_player_from_match(match_id, user_id)
if response == {"status": "success"}:
return "", 204
else:
return jsonify({"message": "Something is fucked"}), 500
except Exception as e:
logger.graylog_logger(level="error", handler="remove_player_from_match", message=e)
return jsonify({"message": "Internal Server Error"}), 500