forked from kinnay/SMB35
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add arguments
- Loading branch information
Showing
15 changed files
with
588 additions
and
534 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") |
Oops, something went wrong.