Skip to content

Commit

Permalink
restructure code
Browse files Browse the repository at this point in the history
add arguments
  • Loading branch information
julthon committed Oct 2, 2022
1 parent ac6270a commit 67171ab
Show file tree
Hide file tree
Showing 15 changed files with 588 additions and 534 deletions.
44 changes: 44 additions & 0 deletions source/AuthenticationServer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from nintendo.nex import settings, kerberos, common, prudp, rmc, \
authentication, secure, utility, notification, messaging, \
ranking2_eagle as ranking2, matchmaking_eagle as matchmaking
import itertools
import secrets
import config

class AuthenticationServer(authentication.AuthenticationServerNX):
def __init__(self, settings):
super().__init__()
self.settings = settings
self.pid = itertools.count(1)

async def validate_and_request_ticket_with_param(self, client, param):
pid = next(self.pid)

key = secrets.token_bytes(16)

result = authentication.ValidateAndRequestTicketResult()
result.pid = pid
result.ticket = self.generate_ticket(pid, config.SERVER_PID, key, config.SERVER_KEY)
result.server_url = common.StationURL(
scheme="prudps", address="0.0.0.1", port=1,
PID=config.SERVER_PID, CID=1, type=2,
sid=2, stream=10
)
result.server_time = common.DateTime.now()
result.server_name = "Super Mario Bros. 35"
result.source_key = key.hex()
return result

def generate_ticket(self, user_pid, server_pid, user_key, server_key):
session_key = secrets.token_bytes(32)

internal = kerberos.ServerTicket()
internal.timestamp = common.DateTime.now()
internal.source = user_pid
internal.session_key = session_key

ticket = kerberos.ClientTicket()
ticket.session_key = session_key
ticket.target = server_pid
ticket.internal = internal.encrypt(server_key, self.settings)
return ticket.encrypt(user_key, self.settings)
26 changes: 26 additions & 0 deletions source/ClientMgr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from nintendo.nex import settings, kerberos, common, prudp, rmc, \
authentication, secure, utility, notification, messaging, \
ranking2_eagle as ranking2, matchmaking_eagle as matchmaking


class ClientMgr:
def __init__(self):
self.clients = {}

def register(self, client):
self.clients[client.pid()] = client

def disconnect(self, client):
pid = client.pid()
if pid in self.clients:
del self.clients[pid]

async def send_message(self, pid, message):
if pid in self.clients:
client = messaging.MessageDeliveryClient(self.clients[pid])
await client.deliver_message(message)

async def send_notification(self, pid, event):
if pid in self.clients:
client = notification.NotificationClient(self.clients[pid])
await client.process_notification_event(event)
150 changes: 150 additions & 0 deletions source/MatchMaker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import base64
import hashlib
import hmac
import itertools
import json
import random
import time

from nintendo.nex import common, notification

import config
import eagle
from MatchmakeSession import MatchmakeSession


class MatchMaker:
def __init__(self, clients, eagle):
self.clients = clients
self.eagle = eagle

self.session_id = itertools.count(1)
self.sessions = {}

def get(self, gid):
if gid not in self.sessions:
raise common.RMCError("RendezVous::SessionVoid")
return self.sessions[gid]

def get_joined(self, gid, pid):
session = self.get(gid)
if pid not in session.participants:
raise common.RMCError("RendezVous::PermissionDenied")
return session

async def send_notification(self, session, event):
for pid in session.participants:
await self.clients.send_notification(pid, event)

async def create(self, session, pid):
session.id = next(self.session_id)
session.host = pid
session.owner = pid
session.started_time = common.DateTime.now()

self.sessions[session.id] = MatchmakeSession(session)

await self.eagle.start(session.id)

async def destroy(self, session, pid):
event = notification.NotificationEvent()
event.pid = pid
event.type = 109000
event.param1 = session.session.id
await self.send_notification(session, event)

del self.sessions[session.session.id]
await self.eagle.stop(session.session.id)

async def join(self, gid, pid, message, participants):
session = self.get(gid)
session.join(pid, message, participants)

event = notification.NotificationEvent()
event.pid = pid
event.type = 3001
event.param1 = gid
event.param2 = pid
event.param3 = participants
event.text = message

await self.clients.send_notification(session.session.owner, event)

payload = {
"expires_at": "%i" % (time.time() + 10800),
"server_env": "lp1",
"server_id": "%i" % gid,
"user_id": "%016x" % pid
}

signature = hmac.digest(
eagle.SIGNATURE_KEY, json.dumps(payload).encode(),
hashlib.sha256
)

token = json.dumps({
"payload": payload,
"signature": base64.b64encode(signature).decode(),
"version": 1
})

event = notification.NotificationEvent()
event.pid = config.SERVER_PID
event.type = 200000
event.param1 = gid
event.map = {
"url": "wss://smb35.ymar.dev:20001/%i" % gid,
"token": base64.b64encode(token.encode()).decode()
}

await self.clients.send_notification(pid, event)

async def leave(self, gid, pid, message="", disconnected=False):
session = self.get(gid)
session.leave(pid)

if pid == session.session.owner:
if session.session.flags & 0x10 and session.participants:
await self.migrate(session)
else:
await self.destroy(session, pid)
else:
event = notification.NotificationEvent()
event.pid = pid
event.type = 3007 if disconnected else 3008
event.param1 = session.session.id
event.param2 = pid
event.text = message

await self.clients.send_notification(session.session.owner, event)

async def migrate(self, session):
new_owner = random.choice(list(session.participants))

event = notification.NotificationEvent()
event.type = 4000
event.pid = session.session.owner
event.param1 = session.session.id
event.param2 = new_owner

session.session.owner = new_owner

await self.send_notification(session, event)

async def disconnect(self, pid):
for session in list(self.sessions.values()):
if pid in session.participants:
await self.leave(session.session.id, pid)

def browse(self, search_criteria):
sessions = []
for session in self.sessions.values():
if session.check(search_criteria):
sessions.append(session.session)

offset = search_criteria.range.offset
if offset == 0xFFFFFFFF:
offset = 0

size = search_criteria.range.size
return sessions[offset:offset + size]
30 changes: 30 additions & 0 deletions source/MatchMakingServer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from nintendo.nex import matchmaking


class MatchMakingServer(matchmaking.MatchMakingServer):
def __init__(self, matchmaker):
super().__init__()
self.matchmaker = matchmaker

async def get_detailed_participants(self, client, gid):
session = self.matchmaker.get_joined(gid, client.pid())

participants = []
for participant in session.participants.values():
details = matchmaking.ParticipantDetails()
details.pid = participant.pid
details.name = str(participant.pid)
details.message = participant.message
details.participants = participant.participants
participants.append(details)
return participants


class MatchMakingServerExt(matchmaking.MatchMakingServerExt):
def __init__(self, matchmaker):
super().__init__()
self.matchmaker = matchmaker

async def end_participation(self, client, gid, message):
await self.matchmaker.leave(gid, client.pid(), message)
return True
35 changes: 35 additions & 0 deletions source/MatchmakeExtensionServer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import random

from nintendo.nex import settings, kerberos, common, prudp, rmc, \
authentication, secure, utility, notification, messaging, \
ranking2_eagle as ranking2, matchmaking_eagle as matchmaking


class MatchmakeExtensionServer(matchmaking.MatchmakeExtensionServer):
def __init__(self, matchmaker):
super().__init__()
self.matchmaker = matchmaker

async def logout(self, client):
await self.matchmaker.disconnect(client.pid())

async def close_participation(self, client, gid):
session = self.matchmaker.get_joined(gid, client.pid())
session.session.open_participation = False

async def auto_matchmake_with_param_postpone(self, client, param):
if param.session.max_participants < param.num_participants:
raise common.RMCError("Core::InvalidArgument")

sessions = []
for crit in param.search_criteria:
sessions += self.matchmaker.browse(crit)

if sessions:
session = random.choice(sessions)
else:
await self.matchmaker.create(param.session, client.pid())
session = param.session

await self.matchmaker.join(session.id, client.pid(), param.join_message, param.num_participants)
return session
5 changes: 5 additions & 0 deletions source/MatchmakeParticipant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class MatchmakeParticipant:
def __init__(self, pid, message, participants):
self.pid = pid
self.message = message
self.participants = participants
49 changes: 49 additions & 0 deletions source/MatchmakeRefereeServer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import itertools

from nintendo.nex import matchmaking, common, notification


class MatchmakeRefereeServer(matchmaking.MatchmakeRefereeServer):
def __init__(self, clients, matchmaker):
super().__init__()
self.clients = clients
self.matchmaker = matchmaker

self.round_id = itertools.count(1)
self.rounds = {}

async def start_round(self, client, param):
if not param.pids: raise common.RMCError("Core::InvalidArgument")

gathering = self.matchmaker.get(param.gid)
if not gathering:
raise common.RMCError("MatchmakeReferee::NotParticipatedGathering")

for pid in param.pids:
if pid not in gathering.participants:
raise common.RMCError("MatchmakeReferee::NotParticipatedGathering")

round_id = next(self.round_id)
self.rounds[round_id] = param

event = notification.NotificationEvent()
event.pid = client.pid()
event.type = 116000
event.param1 = round_id
for pid in param.pids:
await self.clients.send_notification(pid, event)

return round_id

async def get_start_round_param(self, client, round_id):
if round_id not in self.rounds:
raise common.RMCError("MatchmakeReferee::RoundNotFound")
return self.rounds[round_id]

async def end_round(self, client, param):
if param.round_id not in self.rounds:
raise common.RMCError("MatchmakeReferee::RoundNotFound")

async def end_round_with_partial_report(self, client, param):
if param.round_id not in self.rounds:
raise common.RMCError("MatchmakeReferee::RoundNotFound")
Loading

0 comments on commit 67171ab

Please sign in to comment.