Skip to content

Commit

Permalink
Added message system and all known plasma transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
GrzybDev committed Sep 22, 2024
1 parent 3ac79b3 commit 589d4c0
Show file tree
Hide file tree
Showing 64 changed files with 1,793 additions and 67 deletions.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ RUN poetry install --no-root
# Production image, copy all the files and run next
FROM base AS runner

RUN apk add libpq --no-cache

WORKDIR /app

RUN addgroup --system --gid 1001 bfbc2emu
Expand Down
191 changes: 189 additions & 2 deletions bfbc2_masterserver/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import secrets
import uuid
from datetime import datetime
from decimal import Decimal
from typing import Optional
from uu import Error

Expand All @@ -14,6 +15,7 @@
from bfbc2_masterserver.enumerators.ErrorCode import ErrorCode
from bfbc2_masterserver.enumerators.plasma.AssocationType import AssocationType
from bfbc2_masterserver.enumerators.plasma.RecordName import RecordName
from bfbc2_masterserver.enumerators.plasma.StatUpdateType import StatUpdateType
from bfbc2_masterserver.enumerators.theater.GameType import GameType
from bfbc2_masterserver.enumerators.theater.JoinMode import JoinMode
from bfbc2_masterserver.messages.plasma.account.NuGrantEntitlement import (
Expand All @@ -29,6 +31,9 @@
from bfbc2_masterserver.models.plasma.database.Association import Association
from bfbc2_masterserver.models.plasma.database.Entitlement import Entitlement
from bfbc2_masterserver.models.plasma.database.Message import Message
from bfbc2_masterserver.models.plasma.database.MessageAttachment import (
MessageAttachment,
)
from bfbc2_masterserver.models.plasma.database.Persona import Persona
from bfbc2_masterserver.models.plasma.database.Ranking import Ranking
from bfbc2_masterserver.models.plasma.database.Record import Record
Expand Down Expand Up @@ -376,6 +381,16 @@ def persona_get_by_name(self, persona_name: str) -> Persona | ErrorCode:

return persona

def persona_get_owner_id(self, persona_id: int) -> int | ErrorCode:
with Session(self._engine) as session:
persona_query = select(Persona).where(Persona.id == persona_id)
persona = session.exec(persona_query).one_or_none()

if not persona:
return ErrorCode.USER_NOT_FOUND

return persona.owner.id

def persona_add(self, account_id: int, persona: Persona) -> bool | ErrorCode:
with Session(self._engine) as session:
account_query = select(Account).where(Account.id == account_id)
Expand Down Expand Up @@ -407,7 +422,26 @@ def persona_delete(self, account_id: int, persona_id: int) -> bool | ErrorCode:

return True

def assocation_get(
def persona_search(self, search: str) -> list[Persona]:
with Session(self._engine) as session:
search = search.replace("*", "%").replace("_", "")
personas_query = select(Persona).filter(Persona.name.ilike(search)) # type: ignore
personas = session.exec(personas_query).all()

return list(personas)

def association_get(self, persona_id, target_id, association_type: AssocationType):
with Session(self._engine) as session:
association_query = select(Association).where(
Association.owner_id == persona_id,
Association.target_id == target_id,
Association.type == association_type,
)
association = session.exec(association_query).one_or_none()

return association

def association_get_all(
self, persona_id: int, assocation_type: AssocationType
) -> list[Association] | ErrorCode:
with Session(self._engine) as session:
Expand All @@ -422,9 +456,52 @@ def assocation_get(
for association in persona.associations:
if association.type == assocation_type:
assocations_list.append(association)
# Preload the target persona
association.target

return assocations_list

def association_delete(self, association_id: int) -> bool | ErrorCode:
with Session(self._engine) as session:
association_query = select(Association).where(
Association.id == association_id
)
association = session.exec(association_query).one_or_none()

if not association:
return ErrorCode.SYSTEM_ERROR

session.delete(association)
session.commit()

return True

def association_add(
self, owner_id: int, target_id: int, type: AssocationType
) -> Association | ErrorCode:
with Session(self._engine) as session:
association_query = select(Association).where(
Association.owner_id == owner_id,
Association.target_id == target_id,
Association.type == type,
)
existing_association = session.exec(association_query).one_or_none()

if existing_association:
return ErrorCode.SYSTEM_ERROR

association = Association(
owner_id=owner_id,
target_id=target_id,
type=type,
)

session.add(association)
session.commit()
session.refresh(association)

return association

def ranking_get(self, persona_id: int, keys: list[str]) -> list[Ranking]:
with Session(self._engine) as session:
stats_query = select(Ranking).where(Ranking.owner_id == persona_id)
Expand Down Expand Up @@ -483,6 +560,28 @@ def ranking_leaderboard_get(
stats = session.exec(stats_query).all()
return list(stats)

def ranking_set(
self, persona_id: int, key: str, value: Decimal, update_type: StatUpdateType
) -> bool:
with Session(self._engine) as session:
stat_query = select(Ranking).where(
Ranking.owner_id == persona_id, Ranking.key == key
)
stat = session.exec(stat_query).one_or_none()

if not stat:
stat = Ranking(owner_id=persona_id, key=key, value=value)
else:
if update_type == StatUpdateType.RelativeValue:
stat.value += value
else:
stat.value = value

session.add(stat)
session.commit()

return True

def record_get(self, persona_id: int, type: RecordName) -> list[Record]:
with Session(self._engine) as session:
record_query = select(Record).where(Record.owner_id == persona_id)
Expand All @@ -492,13 +591,92 @@ def record_get(self, persona_id: int, type: RecordName) -> list[Record]:

return list(records)

def message_get(self, persona_id: int) -> list[Message]:
def record_add(
self, persona_id: int, type: RecordName, key: str, value: str
) -> bool:
with Session(self._engine) as session:
record = Record(owner_id=persona_id, type=type, key=key, value=value)
session.add(record)
session.commit()

return True

def record_update(
self, persona_id: int, type: RecordName, key: str, value: str
) -> bool:
with Session(self._engine) as session:
record_query = select(Record).where(
Record.owner_id == persona_id, Record.type == type, Record.key == key
)
record = session.exec(record_query).one_or_none()

if not record:
return False

record.value = value

session.add(record)
session.commit()

return True

def message_get(self, message_id: int) -> Message | None:
with Session(self._engine) as session:
message_query = select(Message).where(Message.id == message_id)
message = session.exec(message_query).one_or_none()

return message

def message_get_all(self, persona_id: int) -> list[Message]:
with Session(self._engine) as session:
message_query = select(Message).where(Message.recipient_id == persona_id)
messages = session.exec(message_query).all()

return list(messages)

def message_add(self, messageData: Message) -> Message:
with Session(self._engine) as session:
attachments = []

for attachment in messageData.attachments:
attachment = MessageAttachment(
key=attachment.key, type=attachment.type, data=attachment.data
)

session.add(attachment)
session.commit()

attachments.append(attachment)

message = Message(
sender_id=messageData.sender_id,
recipient_id=messageData.recipient.id,
messageType=messageData.messageType,
deliveryType=messageData.deliveryType,
purgeStrategy=messageData.purgeStrategy,
expiration=messageData.expiration,
attachments=attachments,
)

session.add(message)
session.commit()
session.refresh(message)

return message

def message_delete(self, message_id: int) -> bool:
with Session(self._engine) as session:
message_query = select(Message).where(Message.id == message_id)
message = session.exec(message_query).one_or_none()

if not message:
return False

session.delete(message)
session.commit()

return True

def lobby_get(self, lobby_id: int) -> Lobby | None:
with Session(self._engine) as session:
lobby_query = select(Lobby).where(Lobby.id == lobby_id)
Expand Down Expand Up @@ -709,3 +887,12 @@ def game_get(self, lobby_id: int, game_id: int) -> Game | None:
game = session.exec(game_query).one_or_none()

return game

def game_find(self, gameType: GameType, level: int) -> Game | None:
with Session(self._engine) as session:
game_query = select(Game).where(
Game.gameType == gameType, Game.gameLevel == level
)
game = session.exec(game_query).one_or_none()

return game
1 change: 1 addition & 0 deletions bfbc2_masterserver/dataclasses/Manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from redis import Redis

from bfbc2_masterserver.database import DatabaseAPI
from bfbc2_masterserver.dataclasses.Client import Client

Expand Down
2 changes: 2 additions & 0 deletions bfbc2_masterserver/dataclasses/plasma/Service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def __init__(self, plasma: BasePlasmaHandler) -> None:

self.connection = plasma.client.connection
self.database = plasma.manager.database
self.manager = plasma.manager
self.redis = plasma.manager.redis
self.plasma = plasma

@abstractmethod
Expand Down
41 changes: 41 additions & 0 deletions bfbc2_masterserver/enumerators/fesl/FESLTransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@


class FESLTransaction(Enum):
# Universal
TransactionException = "TransactionException"

# Plasma (Connect)
Hello = "Hello"
MemCheck = "MemCheck"
GetPingSites = "GetPingSites"
Ping = "Ping"
Goodbye = "Goodbye"
# Never called during my tests, but implemented in game code
Suicide = "Suicide"

# Plasma (Account)
Expand All @@ -26,28 +30,65 @@ class FESLTransaction(Enum):
NuEntitleUser = "NuEntitleUser"
NuLookupUserInfo = "NuLookupUserInfo"
NuGrantEntitlement = "NuGrantEntitlement"
NuSearchOwners = "NuSearchOwners"
# Never called during my tests, but implemented in game code
NuCreateEncryptedToken = "NuCreateEncryptedToken"
NuSuggestPersonas = "NuSuggestPersonas"
NuUpdatePassword = "NuUpdatePassword"
NuGetAccount = "NuGetAccount"
NuGetAccountByNuid = "NuGetAccountByNuid"
NuGetAccountByPS3Ticket = "NuGetAccountByPS3Ticket" # PS3 Only?
NuUpdateAccount = "NuUpdateAccount"
GameSpyPreAuth = "GameSpyPreAuth" # GameSpy leftover?
NuXBL360Login = "NuXBL360Login" # Xbox 360 Only?
NuXBL360AddAccount = "NuXBL360AddAccount" # Xbox 360 Only?
NuPS3Login = "NuPS3Login" # PS3 Only?
NuPS3AddAccount = "NuPS3AddAccount" # PS3 Only?
NuGetEntitlementCount = "NuGetEntitlementCount"

# Plasma (Assocation)
GetAssociations = "GetAssociations"
AddAssociations = "AddAssociations"
NotifyAssociationUpdate = "NotifyAssociationUpdate"
DeleteAssociations = "DeleteAssociations"
GetAssociationsCount = "GetAssociationsCount"

# Plasma (Extensible Message)
ModifySettings = "ModifySettings"
GetMessages = "GetMessages"
SendMessage = "SendMessage"
AsyncMessageEvent = "AsyncMessageEvent"
GetMessageAttachments = "GetMessageAttachments"
DeleteMessages = "DeleteMessages"
PurgeMessages = "PurgeMessages"
AsyncPurgedEvent = "AsyncPurgedEvent"

# Plasma (PlayNow)
Start = "Start"
Status = "Status"

# Plasma (Presence)
SetPresenceStatus = "SetPresenceStatus"
PresenceSubscribe = "PresenceSubscribe"
PresenceUnsubscribe = "PresenceUnsubscribe"
AsyncPresenceStatusEvent = "AsyncPresenceStatusEvent"

# Plasma (Ranking)
GetStats = "GetStats"
GetRankedStatsForOwners = "GetRankedStatsForOwners"
GetRankedStats = "GetRankedStats"
GetTopNAndStats = "GetTopNAndStats"
UpdateStats = "UpdateStats"
# Never called during my tests, but implemented in game code
GetStatsForOwners = "GetStatsForOwners"
GetTopN = "GetTopN"
GetTopNAndMe = "GetTopNAndMe"
GetDateRange = "GetDateRange"

# Plasma (Record)
GetRecordAsMap = "GetRecordAsMap"
GetRecord = "GetRecord"
AddRecord = "AddRecord"
UpdateRecord = "UpdateRecord"
AddRecordAsMap = "AddRecordAsMap"
UpdateRecordAsMap = "UpdateRecordAsMap"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum


class AssocationUpdateOperation(Enum):
ADD = "add"
DEL = "del"
6 changes: 6 additions & 0 deletions bfbc2_masterserver/enumerators/plasma/ListFullBehavior.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum


class ListFullBehavior(Enum):
ReturnError = "ReturnError"
RollLeastRecentlyModified = "RollLeastRecentlyModified"
7 changes: 7 additions & 0 deletions bfbc2_masterserver/enumerators/plasma/StatUpdateType.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from enum import Enum


class StatUpdateType(Enum):
AbsoluteValueRounded = 0
AbsoluteValue = 1
RelativeValue = 3
9 changes: 9 additions & 0 deletions bfbc2_masterserver/messages/plasma/account/GameSpyPreAuth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from bfbc2_masterserver.models.general.PlasmaTransaction import PlasmaTransaction


class GameSpyPreAuthRequest(PlasmaTransaction):
pass


class GameSpyPreAuthResponse(PlasmaTransaction):
pass
Loading

0 comments on commit 589d4c0

Please sign in to comment.