Initial commit

fluent_nhibernate
Tiger 2023-09-23 13:11:07 +02:00
commit 6c4de34920
34 changed files with 2269 additions and 0 deletions

364
.gitignore vendored Normal file
View File

@ -0,0 +1,364 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
.idea/

View File

@ -0,0 +1,9 @@
using Tiger.Communication.Messages.Types;
using Tiger.Networking.Game.Sessions;
namespace Tiger.Communication.Messages;
public interface IMessageHandler
{
Task TryHandleAsync(GameSession session, ClientMessage request);
}

View File

@ -0,0 +1,28 @@
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Outgoing.Handshake;
using Tiger.Communication.Messages.Types;
using Tiger.Networking.Game.Sessions;
namespace Tiger.Communication.Messages.Incoming.Handshake;
public class InfoRetrieveEvent : IMessageEvent
{
private readonly IGameSessionManager _gameSessionManager;
public InfoRetrieveEvent(IGameSessionManager gameSessionManager)
{
_gameSessionManager = gameSessionManager;
}
public IncomingHeaders Header => IncomingHeaders.InfoRetrieveEvent;
public async Task HandleAsync(GameSession gameSession, ClientMessage request)
{
if (gameSession.Habbo == null)
{
await _gameSessionManager.CloseAsync("Not logged in", gameSession);
return;
}
await gameSession.SendComposerAsync(new UserObjectMessageComposer(gameSession.Habbo));
}
}

View File

@ -0,0 +1,52 @@
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Outgoing.Generic.Alerts;
using Tiger.Communication.Messages.Outgoing.Handshake;
using Tiger.Communication.Messages.Types;
using Tiger.Game.Habbos;
using Tiger.Game.Settings;
using Tiger.Networking.Game.Sessions;
namespace Tiger.Communication.Messages.Incoming.Handshake;
public class SsoTicketEvent : IMessageEvent
{
private readonly IHabboDao _habboDao;
private readonly IGameSessionManager _gameSessionManager;
private readonly ISettingsManager _settingsManager;
public SsoTicketEvent(IHabboDao habboDao, IGameSessionManager gameSessionManager, ISettingsManager settingsManager)
{
_habboDao = habboDao;
_gameSessionManager = gameSessionManager;
_settingsManager = settingsManager;
}
public IncomingHeaders Header => IncomingHeaders.SSoTicketEvent;
public async Task HandleAsync(GameSession gameSession, ClientMessage request)
{
var sso = request.ReadString();
if (sso == null)
{
await _gameSessionManager.CloseAsync("Malformed packet", gameSession);
return;
}
var habbo = await _habboDao.GetHabboBySsoAsync(sso);
if (habbo == null)
{
await _gameSessionManager.CloseAsync("User not found", gameSession);
return;
}
gameSession.Habbo = habbo;
await gameSession.SendComposerAsync(new AuthenticationOkMessageComposer());
await gameSession.SendComposerAsync(new HabboBroadcastMessageComposer(
_settingsManager.GetSetting<string>("welcome.message")
.Replace("{user}", gameSession.Habbo.Username)
));
}
}

View File

@ -0,0 +1,491 @@
namespace Tiger.Communication.Messages.Incoming;
public enum IncomingHeaders : short
{
AcceptFriendEvent = 137,
AcceptTradingEvent = 3863,
AddAdminRightsToMemberEvent = 2894,
AddFavouriteRoomEvent = 3817,
AddItemToTradeEvent = 3107,
AddItemsToTradeEvent = 1263,
AddJukeboxDiskEvent = 753,
AddSpamWallPostItEvent = 3283,
AmbassadorAlertEvent = 2996,
AnswerPollEvent = 3505,
ApplySnapshotEvent = 3373,
ApproveMembershipRequestEvent = 3386,
ApproveNameEvent = 2109,
AssignRightsEvent = 808,
AvatarEffectActivatedEvent = 2959,
AvatarEffectSelectedEvent = 1752,
AvatarExpressionEvent = 2456,
BanUserWithDurationEvent = 1477,
BreedPetsEvent = 1638,
BuildersClubPlaceWallItemEvent = 2462,
BuildersClubQueryFurniCountEvent = 2529,
BuyMarketplaceOfferEvent = 1603,
CallForHelpEvent = 1691,
CallForHelpFromForumMessageEvent = 1412,
CallForHelpFromForumThreadEvent = 534,
CallForHelpFromImEvent = 2950,
CallForHelpFromPhotoEvent = 2492,
CanCreateRoomEvent = 2128,
CancelMarketplaceOfferEvent = 434,
CancelPetBreedingEvent = 2713,
CancelTypingEvent = 1474,
ChangeMottoEvent = 2228,
ChangePostureEvent = 2235,
ChangeUserNameEvent = 2977,
ChatEvent = 1314,
ChatReviewGuideDecidesOnOfferEvent = 3365,
ChatReviewGuideDetachedEvent = 2501,
ChatReviewGuideVoteEvent = 3961,
ChatReviewSessionCreateEvent = 3060,
CheckUserNameEvent = 3950,
ClientHelloEvent = 4000,
CloseIssueDefaultActionEvent = 2717,
CloseIssuesEvent = 2067,
CloseTradingEvent = 2551,
CommandBotEvent = 2624,
CompleteDiffieHandshakeEvent = 773,
CompostPlantEvent = 3835,
ConfirmAcceptTradingEvent = 2760,
ConfirmDeclineTradingEvent = 2341,
ConfirmPetBreedingEvent = 3382,
ControlYoutubeDisplayPlaybackEvent = 3005,
CraftEvent = 3591,
CraftSecretEvent = 1251,
CreateFlatEvent = 2752,
CreateGuildEvent = 230,
CreditFurniRedeemEvent = 3115,
CustomizeAvatarWithFurniEvent = 3374,
CustomizePetWithFurniEvent = 1328,
DanceEvent = 2080,
DeactivateGuildEvent = 1134,
DeclineFriendEvent = 2890,
DeleteFavouriteRoomEvent = 309,
DeleteRoomEvent = 532,
DeselectFavouriteHabboGroupEvent = 1820,
DiceOffEvent = 1533,
DropCarryItemEvent = 2814,
EditEventEvent = 3991,
EnterOneWayDoorEvent = 2765,
EventLogEvent = 3457,
FindNewFriendsEvent = 516,
FollowFriendEvent = 2970,
ForwardToSomeRoomEvent = 1703,
FriendFurniConfirmLockEvent = 3775,
Game2GetAccountGameStatusEvent = 11,
GameUnloadedEvent = 3207,
GetAchievementsEvent = 219,
GetBadgePointLimitsEvent = 1371,
GetBadgesEvent = 2769,
GetBannedUsersFromRoomEvent = 2267,
GetBonusRareInfoEvent = 957,
GetBotCommandConfigurationDataEvent = 1986,
GetBotInventoryEvent = 3848,
GetBundleDiscountRulesetEvent = 223,
GetCatalogIndexEvent = 1195,
GetCatalogPageEvent = 412,
GetCfhChatlogEvent = 211,
GetCfhStatusEvent = 2746,
GetClubGiftInfo = 487,
GetClubOffersEvent = 3285,
GetCraftableProductsEvent = 633,
GetCraftingRecipeEvent = 1173,
GetCraftingRecipesAvailableEvent = 3086,
GetCreditsInfoEvent = 273,
GetCurrentTimingCodeEvent = 2912,
GetCustomRoomFilterEvent = 1911,
GetExtendedProfileEvent = 3265,
GetFlatControllersEvent = 3385,
GetForumStatsEvent = 3149,
GetForumsListEvent = 873,
GetFriendRequestsEvent = 2448,
GetFurnitureAliasesEvent = 3898,
GetGameListEvent = 741,
GetGameStatusEvent = 3171,
GetGiftWrappingConfigurationEvent = 418,
GetGuestRoomEvent = 2230,
GetGuideReportingStatusEvent = 3786,
GetGuildCreationInfoEvent = 798,
GetGuildEditInfoEvent = 1004,
GetGuildEditorDataEvent = 813,
GetGuildFurniContextMenuInfoEvent = 2651,
GetGuildMembersEvent = 312,
GetGuildMembershipsEvent = 367,
GetHabboGroupBadgesEvent = 21,
GetHabboGroupDetailsEvent = 2991,
GetIgnoredUsersEvent = 3878,
GetItemDataEvent = 3964,
GetJukeboxPlayListEvent = 1435,
GetLimitedOfferAppearingNextEvent = 410,
GetMotdEvent = 1523,
GetMarketplaceCanMakeOfferEvent = 848,
GetMarketplaceConfigurationEvent = 2597,
GetMarketplaceItemStatsEvent = 3288,
GetMarketplaceOffersEvent = 2407,
GetMarketplaceOwnOffersEvent = 2105,
GetMemberGuildItemCountEvent = 3593,
GetMessagesEvent = 232,
GetModeratorRoomInfoEvent = 707,
GetModeratorUserInfoEvent = 3295,
GetNowPlayingEvent = 1325,
GetOccupiedTilesEvent = 1687,
GetOfficialSongIdEvent = 3189,
GetPendingCallsForHelpEvent = 3267,
GetPetCommandsEvent = 2161,
GetPetInfoEvent = 2934,
GetPetInventoryEvent = 3095,
GetPopularRoomTagsEvent = 826,
GetProductOfferEvent = 2594,
GetPromoArticlesEvent = 1827,
GetRecyclerPrizesEvent = 398,
GetRecyclerStatusEvent = 1342,
GetRelationshipStatusInfoEvent = 2138,
GetResolutionAchievementsEvent = 359,
GetRoomAdPurchaseInfoEvent = 1075,
GetRoomChatlogEvent = 2587,
GetRoomEntryDataEvent = 2300,
GetRoomEntryTileEvent = 3559,
GetRoomSettingsEvent = 3129,
GetRoomVisitsEvent = 3526,
GetSelectedBadgesEvent = 2091,
GetSellablePetPalettesEvent = 1756,
GetSongInfoEvent = 3082,
GetSoundSettingsEvent = 2388,
GetTalentTrackEvent = 196,
GetTalentTrackLevelEvent = 2127,
GetThreadsEvent = 436,
GetUnreadForumsCountEvent = 2908,
GetUserChatlogEvent = 1391,
GetUserEventCatsEvent = 1782,
GetUserFlatCatsEvent = 3027,
GetUserTagsEvent = 17,
GetWardrobeEvent = 2742,
GetWeeklyGameRewardEvent = 2914,
GetWeeklyGameRewardWinnersEvent = 1054,
GetYoutubeDisplayStatusEvent = 336,
GuideSessionCreateEvent = 3338,
GuideSessionFeedbackEvent = 477,
GuideSessionGetRequesterRoomEvent = 1052,
GuideSessionGuideDecidesEvent = 1424,
GuideSessionInviteRequesterEvent = 234,
GuideSessionIsTypingEvent = 519,
GuideSessionMessageEvent = 3899,
GuideSessionOnDutyUpdateEvent = 1922,
GuideSessionReportEvent = 3969,
GuideSessionRequesterCancelsEvent = 291,
GuideSessionResolvedEvent = 887,
HabboSearchEvent = 1210,
// HotelViewClaimBadgeRewardEvent = -1,
HotelViewRequestBadgeRewardEvent = 2318,
HotelViewRequestSecondsUntilEvent = 271,
IgnoreUserEvent = 1117,
InfoRetrieveEvent = 357,
InitDiffieHandshakeEvent = 3110,
JoinHabboGroupEvent = 998,
JoinQueueEvent = 1458,
JukeBoxEventOne = 2304,
KickMemberEvent = 593,
LatencyPingRequestEvent = 295,
LetUserInEvent = 1644,
LookToEvent = 3301,
MakeOfferEvent = 3447,
MessengerInitEvent = 2781,
ModAlertEvent = 229,
ModBanEvent = 2766,
ModKickEvent = 2582,
ModMessageEvent = 1840,
ModMuteEvent = 1945,
// ModToolRequestRoomUserChatlogEvent = -1,
ModToolSanctionEvent = 1392,
// ModToolWarnEvent = -1,
ModTradingLockEvent = 3742,
ModerateMessageEvent = 286,
ModerateRoomEvent = 3260,
ModerateThreadEvent = 1397,
ModeratorActionEvent = 3842,
MountPetEvent = 1036,
MoveAvatarEvent = 3320,
MoveObjectEvent = 248,
MovePetEvent = 3449,
MoveWallItemEvent = 168,
MuteAllInRoomEvent = 3637,
MyFavouriteRoomsSearchEvent = 2578,
MyFriendsRoomsSearchEvent = 2266,
MyGuildBasesSearchEvent = 39,
MyRoomHistorySearchEvent = 2264,
MyRoomRightsSearchEvent = 272,
MyRoomsSearchEvent = 2277,
NavigatorAddCollapsedCategoryEvent = 1834,
NavigatorAddSavedSearchEvent = 2226,
NavigatorDeleteSavedSearchEvent = 1954,
NavigatorRemoveCollapsedCategoryEvent = 637,
NavigatorSetSearchCodeViewModeEvent = 1202,
NewNavigatorInitEvent = 2110,
NewNavigatorSearchEvent = 249,
NewUserExperienceGetGiftsEvent = 1822,
NewUserExperienceScriptProceedEvent = 1299,
OpenCampaignCalendarDoorEvent = 8809,
OpenCampaignCalendarDoorAsStaffEvent = 2507,
OpenFlatConnectionEvent = 2312,
OpenPetPackageEvent = 3698,
OpenTradingEvent = 1481,
PassCarryItemEvent = 2941,
PickIssuesEvent = 15,
PickupObjectEvent = 3456,
PlaceBotEvent = 1592,
PlaceObjectEvent = 1258,
PlacePetEvent = 2647,
PlacePostItEvent = 2248,
PollRejectEvent = 1773,
PollStartEvent = 109,
PongEvent = 2596,
PopularRoomsSearchEvent = 2758,
PostMessageEvent = 3529,
PresentOpenEvent = 3558,
PublishPhotoEvent = 2068,
PurchaseFromCatalogAsGiftEvent = 1411,
PurchaseFromCatalogEvent = 3492,
PurchasePhotoEvent = 2408,
PurchaseRoomAdEvent = 777,
PurchaseTargetedOfferEvent = 1826,
PurchaseVipMembershipExtensionEvent = 3407,
QuitEvent = 105,
RateFlatEvent = 3582,
RecycleItemsEvent = 2771,
RedeemMarketplaceOfferCreditsEvent = 2650,
RedeemVoucherEvent = 339,
RejectMembershipRequestEvent = 1894,
ReleaseIssuesEvent = 1572,
RemoveAdminRightsFromMemberEvent = 722,
RemoveAllRightsEvent = 2683,
RemoveBotFromFlatEvent = 3323,
RemoveFriendEvent = 1689,
RemoveItemEvent = 3336,
RemoveItemFromTradeEvent = 3845,
RemoveJukeboxDiskEvent = 3050,
RemoveOwnRoomRightsRoomEvent = 3182,
RemovePetFromFlatEvent = 1581,
RemoveRightsEvent = 2064,
RemoveSaddleFromPetEvent = 186,
RenderRoomEvent = 3226,
RenderRoomThumbnailEvent = 1982,
RentableSpaceCancelRentEvent = 1667,
RentableSpaceRentEvent = 2946,
// RequestAchievementConfigurationEvent = -1,
RequestCameraConfigurationEvent = 796,
RequestFriendEvent = 3157,
RequestFurniInventoryEvent = 3150,
RequestFurniInventoryWhenNotInRoomEvent = 3500,
RequestRoomPropertySet = 711,
RespectPetEvent = 3202,
RespectUserEvent = 2694,
RoomDimmerChangeStateEvent = 2296,
RoomDimmerGetPresetsEvent = 2813,
RoomDimmerSavePresetEvent = 1648,
RoomTextSearchEvent = 3943,
RoomUserKickEvent = 1320,
RoomUserMuteEvent = 3485,
RoomsWhereMyFriendsAreSearchEvent = 1786,
RoomsWithHighestScoreSearchEvent = 2939,
SSoTicketEvent = 2419,
SaveRoomSettingsEvent = 1969,
SaveWardrobeOutfitEvent = 800,
ScrGetKickbackInfoEvent = 869,
ScrGetUserInfoEvent = 3166,
// SearchRoomsByTagEvent = -1,
SelectClubGiftEvent = 2276,
SelectFavouriteHabboGroupEvent = 3549,
SendMsgEvent = 3567,
SendRoomInviteEvent = 1276,
SetActivatedBadgesEvent = 644,
SetChatPreferencesEvent = 1262,
SetChatStylePreferenceEvent = 1030,
SetClothingChangeDataEvent = 924,
SetCustomStackingHeightEvent = 3839,
SetIgnoreRoomInvitesEvent = 1086,
SetItemDataEvent = 3666,
SetMannequinFigureEvent = 2209,
SetMannequinNameEvent = 2850,
SetNewNavigatorWindowPreferencesEvent = 3159,
SetObjectDataEvent = 3608,
SetRandomStateEvent = 3617,
SetRelationshipStatusEvent = 3768,
SetRoomBackgroundColorDataEvent = 2880,
SetRoomCameraPreferencesEvent = 1461,
SetSoundSettingsEvent = 1367,
SetTargetedOfferStateEvent = 2041,
SetYoutubeDisplayPlaylistEvent = 2069,
ShoutEvent = 2085,
SignEvent = 1975,
SpinWheelOfFortuneEvent = 2144,
StartTypingEvent = 1597,
SubmitRoomToCompetitionEvent = 2595,
ThrowDiceEvent = 1990,
TogglePetBreedingPermissionEvent = 3379,
TogglePetRidingPermissionEvent = 1472,
ToggleStaffPickEvent = 1918,
UnacceptTradingEvent = 1444,
UnbanUserFromRoomEvent = 992,
UnignoreUserEvent = 2061,
UniqueIdEvent = 2490,
UpdateActionEvent = 2281,
UpdateConditionEvent = 3203,
UpdateFigureDataEvent = 2730,
UpdateFloorPropertiesEvent = 875,
UpdateForumSettingsEvent = 2214,
UpdateGuildBadgeEvent = 1991,
UpdateGuildColorsEvent = 1764,
UpdateGuildIdentityEvent = 3137,
UpdateGuildSettingsEvent = 3435,
UpdateHomeRoomEvent = 1740,
UpdateRoomFilterEvent = 3001,
UpdateThreadEvent = 3045,
UpdateTriggerEvent = 1520,
UpdateUiFlagsEvent = 2313,
UseFurnitureEvent = 99,
UseWallItemEvent = 210,
VisitUserEvent = 3997,
WhisperEvent = 1543,
AcceptGameInviteEvent = 3802,
AcceptQuestEvent = 3604,
ApproveAllMembershipRequestsEvent = 882,
BuildersClubPlaceRoomItemEvent = 1051,
BuyMarketplaceTokensEvent = 1866,
CallForHelpFromSelfieEvent = 2755,
CancelEventEvent = 2725,
CancelQuestEvent = 3133,
ChangeEmailEvent = 3965,
ChangeQueueEvent = 3093,
CommunityGoalVoteEvent = 3536,
CompetitionRoomsSearchEvent = 433,
DeletePendingCallsForHelpEvent = 3605,
DisconnectEvent = 2445,
ExtendRentOrBuyoutFurniEvent = 1071,
ExtendRentOrBuyoutStripItemEvent = 2115,
ForwardToACompetitionRoomEvent = 172,
ForwardToARandomPromotedRoomEvent = 10,
ForwardToRandomCompetitionRoomEvent = 865,
FriendListUpdateEvent = 1419,
FriendRequestQuestCompleteEvent = 1148,
Game2CheckGameDirectoryStatusEvent = 3259,
Game2ExitGameEvent = 1445,
Game2GameChatEvent = 2502,
Game2GetWeeklyFriendsLeaderboardEvent = 1232,
Game2GetWeeklyLeaderboardEvent = 2565,
Game2LoadStageReadyEvent = 2415,
Game2PlayAgainEvent = 3196,
Game2RequestFullStatusUpdateEvent = 1598,
GetCatalogPageExpirationEvent = 742,
GetCatalogPageWithEarliestExpiryEvent = 3135,
GetCategoriesWithUserCountEvent = 3782,
GetCommunityGoalEarnedPrizesEvent = 2688,
GetCommunityGoalHallOfFameEvent = 2167,
GetCommunityGoalProgressEvent = 1145,
GetConcurrentUsersGoalProgressEvent = 1343,
GetConcurrentUsersRewardEvent = 3872,
GetDailyQuestEvent = 2486,
GetDirectClubBuyAvailableEvent = 801,
GetEmailStatusEvent = 2557,
GetExtendedProfileByNameEvent = 2249,
GetFaqCategoryEvent = 3445,
GetFaqTextEvent = 1849,
GetGameAchievementsEvent = 2399,
GetHabboBasicMembershipExtendOfferEvent = 603,
GetInterstitialEvent = 2519,
GetIsBadgeRequestFulfilledEvent = 1364,
GetIsOfferGiftableEvent = 1347,
GetIsUserPartOfCompetitionEvent = 2077,
GetNextTargetedOfferEvent = 2487,
GetOfficialRoomsEvent = 1229,
GetQuestsEvent = 3333,
GetQuizQuestionsEvent = 1296,
GetSeasonalCalendarDailyOfferEvent = 3257,
GetSeasonalQuestsOnlyEvent = 1190,
GetSoundMachinePlayListEvent = 3498,
GetTargetedOfferEvent = 596,
GetThreadEvent = 3900,
GetUserGameAchievementsEvent = 389,
GiveSupplementToPetEvent = 749,
GoToFlatEvent = 685,
GuideAdvertisementReadEvent = 2455,
GuildBaseSearchEvent = 2930,
HarvestPetEvent = 1521,
IgnoreUserIdEvent = 3314,
InterstitialShownEvent = 1109,
LagWarningReportEvent = 3847,
LatencyPingReportEvent = 96,
LeaveQueueEvent = 2384,
ModToolPreferencesEvent = 31,
MyFrequentRoomHistorySearchEvent = 1002,
MyRecommendedRoomsEvent = 2537,
MysteryBoxWaitingCanceledEvent = 2012,
OpenMysteryTrophyEvent = 3074,
OpenQuestTrackerEvent = 2750,
OpenWelcomeGiftEvent = 2638,
PassCarryItemToPetEvent = 2768,
PeerUsersClassificationEvent = 1160,
PerformanceLogEvent = 3230,
PetSelectedEvent = 549,
PhotoCompetitionEvent = 3959,
PostQuizAnswersEvent = 3720,
PurchaseBasicMembershipExtensionEvent = 2735,
RedeemCommunityGoalPrizeEvent = 90,
RejectQuestEvent = 2397,
RentableSpaceStatusEvent = 872,
RequestABadgeEvent = 3077,
ResetPhoneNumberStateEvent = 2741,
ResetResolutionAchievementEvent = 3144,
ResetUnseenItemIdsEvent = 3493,
ResetUnseenItemsEvent = 2343,
RoomAdEventTabAdClickedEvent = 2412,
RoomAdEventTabViewedEvent = 2668,
RoomAdPurchaseInitiatedEvent = 2283,
RoomAdSearchEvent = 2809,
RoomCompetitionInitEvent = 1334,
RoomNetworkOpenConnectionEvent = 3736,
RoomUsersClassificationEvent = 2285,
SearchFaqsEvent = 2031,
SetPhoneNumberVerificationStatusEvent = 1379,
SetRoomSessionTagsEvent = 3305,
ShopTargetedOfferViewedEvent = 3483,
StartCampaignEvent = 1697,
TryPhoneNumberEvent = 790,
UnblockGroupMemberEvent = 2864,
UpdateForumReadMarkerEvent = 1855,
UpdateRoomCategoryAndTradeSettingsEvent = 1265,
UpdateRoomThumbnailEvent = 2468,
VerifyCodeEvent = 2721,
VersionCheckEvent = 1053,
VoteForRoomEvent = 143,
WelcomeGiftChangeEmailEvent = 66,
UnknownSnowStormEvent6000 = 6000,
UnknownSnowStormEvent6001 = 6001,
UnknownSnowStormEvent6002 = 6002,
UnknownSnowStormEvent6003 = 6003,
UnknownSnowStormEvent6004 = 6004,
UnknownSnowStormEvent6005 = 6005,
UnknownSnowStormEvent6006 = 6006,
UnknownSnowStormEvent6007 = 6007,
UnknownSnowStormEvent6008 = 6008,
UnknownSnowStormEvent6009 = 6009,
UnknownSnowStormEvent6010 = 6010,
UnknownSnowStormEvent6011 = 6011,
SnowStormJoinQueueEvent = 6012,
UnknownSnowStormEvent6013 = 6013,
UnknownSnowStormEvent6014 = 6014,
UnknownSnowStormEvent6015 = 6015,
UnknownSnowStormEvent6016 = 6016,
UnknownSnowStormEvent6017 = 6017,
UnknownSnowStormEvent6018 = 6018,
UnknownSnowStormEvent6019 = 6019,
UnknownSnowStormEvent6020 = 6020,
UnknownSnowStormEvent6021 = 6021,
UnknownSnowStormEvent6022 = 6022,
UnknownSnowStormEvent6023 = 6023,
UnknownSnowStormEvent6024 = 6024,
UnknownSnowStormEvent6025 = 6025,
SnowStormUserPickSnowballEvent = 6026,
}

View File

@ -0,0 +1,10 @@
using Tiger.Communication.Messages.Outgoing;
using Tiger.Communication.Messages.Types;
namespace Tiger.Communication.Messages.Interfaces;
public interface IMessageComposer
{
OutgoingHeaders Header { get; }
void Compose(ServerMessage message);
}

View File

@ -0,0 +1,11 @@
using Tiger.Communication.Messages.Incoming;
using Tiger.Communication.Messages.Types;
using Tiger.Networking.Game.Sessions;
namespace Tiger.Communication.Messages.Interfaces;
public interface IMessageEvent
{
IncomingHeaders Header { get; }
Task HandleAsync(GameSession gameSession, ClientMessage request);
}

View File

@ -0,0 +1,46 @@
using Microsoft.Extensions.Logging;
using Tiger.Communication.Messages.Incoming;
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Types;
using Tiger.Networking.Game.Sessions;
namespace Tiger.Communication.Messages;
public class MessageHandler : IMessageHandler
{
private readonly ILogger<MessageHandler> _logger;
private readonly IDictionary<short, IMessageEvent> _messageEvents;
private readonly IDictionary<short, string> _messageNames;
public MessageHandler(ILogger<MessageHandler> logger, IEnumerable<IMessageEvent> messageEvents)
{
_logger = logger;
_messageEvents = messageEvents.ToDictionary(messageEvent => (short)messageEvent.Header);
_messageNames = new Dictionary<short, string>();
foreach (var name in Enum.GetNames(typeof(IncomingHeaders)))
{
_messageNames.Add((short)Enum.Parse(typeof(IncomingHeaders), name), name);
}
}
public async Task TryHandleAsync(GameSession session, ClientMessage request)
{
if (request.Header == null)
{
return;
}
if (_messageEvents.TryGetValue(request.Header.Value, out var messageEvent))
{
_logger.LogInformation("Handling header ID {Header} on class {Class}", request.Header.Value, messageEvent.GetType());
await messageEvent.HandleAsync(session, request);
}
else
{
_logger.LogWarning("Unregistered header {Header} {Name}", request.Header.Value, _messageNames.TryGetValue(request.Header.Value, out var messageName) ? messageName : string.Empty);
}
}
}

View File

@ -0,0 +1,20 @@
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Types;
namespace Tiger.Communication.Messages.Outgoing.Generic.Alerts;
public class HabboBroadcastMessageComposer : IMessageComposer
{
private readonly string _welcomeMessage;
public HabboBroadcastMessageComposer(string welcomeMessage)
{
_welcomeMessage = welcomeMessage;
}
public OutgoingHeaders Header => OutgoingHeaders.HabboBroadcastMessageComposer;
public void Compose(ServerMessage message)
{
message.AppendString(_welcomeMessage);
}
}

View File

@ -0,0 +1,13 @@
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Types;
namespace Tiger.Communication.Messages.Outgoing.Handshake;
public class AuthenticationOkMessageComposer : IMessageComposer
{
public OutgoingHeaders Header => OutgoingHeaders.AuthenticationOkMessageComposer;
public void Compose(ServerMessage message)
{
}
}

View File

@ -0,0 +1,35 @@
using System.Globalization;
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Types;
using Tiger.Game.Habbos;
namespace Tiger.Communication.Messages.Outgoing.Handshake;
public class UserObjectMessageComposer : IMessageComposer
{
private readonly Habbo _habbo;
public UserObjectMessageComposer(Habbo habbo)
{
_habbo = habbo;
}
public OutgoingHeaders Header => OutgoingHeaders.UserObjectComposer;
public void Compose(ServerMessage message)
{
message.AppendUInt32(_habbo.Id);
message.AppendString(_habbo.Username);
message.AppendString(_habbo.Figure);
message.AppendString(_habbo.Gender);
message.AppendString(_habbo.Motto);
message.AppendString(string.Empty);
message.AppendBoolean(false);
message.AppendInt32(0); // respect received
message.AppendInt32(0); // respect points to give
message.AppendInt32(0); // scratch to give
message.AppendBoolean(false);
message.AppendString(_habbo.LastLogin.ToString(CultureInfo.CurrentCulture));
message.AppendBoolean(false); // can change name
message.AppendBoolean(false); // safety locked
}
}

View File

@ -0,0 +1,507 @@
namespace Tiger.Communication.Messages.Outgoing;
public enum OutgoingHeaders
{
AcceptFriendResultComposer = 896,
AccountPreferencesComposer = 513,
AccountSafetyLockStatusChangeMessageComposer = 1243,
AchievementComposer = 2107,
AchievementResolutionCompletedMessageComposer = 740,
AchievementResolutionProgressMessageComposer = 3370,
AchievementResolutionsMessageComposer = 66,
AchievementsComposer = 305,
AchievementsScoreComposer = 1968,
ActivityPointsMessageComposer = 2018,
ApproveNameMessageComposer = 1503,
AuthenticationOkMessageComposer = 2491,
AvailabilityStatusMessageComposer = 2033,
AvailabilityTimeMessageComposer = 600,
AvatarEffectActivatedMessageComposer = 1959,
AvatarEffectAddedMessageComposer = 2867,
AvatarEffectExpiredMessageComposer = 2228,
AvatarEffectMessageComposer = 1167,
AvatarEffectSelectedMessageComposer = 3473,
AvatarEffectsMessageComposer = 340,
BadgePointLimitsComposer = 2501,
BadgeReceivedComposer = 2493,
BadgesComposer = 717,
BannedUsersFromRoomComposer = 1869,
BonusRareInfoMessageComposer = 1533,
BotAddedToInventoryComposer = 1352,
BotCommandConfigurationComposer = 1618,
BotErrorComposer = 639,
BotForceOpenContextMenuComposer = 296,
BotInventoryComposer = 3086,
BotReceivedMessageComposer = 3684,
BotRemovedFromInventoryComposer = 233,
BotSkillListUpdateComposer = 69,
BuildersClubFurniCountMessageComposer = 3828,
BuildersClubSubscriptionStatusMessageComposer = 1452,
BundleDiscountRulesetMessageComposer = 2347,
CallForHelpDisabledNotifyMessageComposer = 1651,
CallForHelpPendingCallsDeletedMessageComposer = 77,
CallForHelpPendingCallsMessageComposer = 1121,
CallForHelpReplyMessageComposer = 3796,
CallForHelpResultMessageComposer = 3635,
CameraPublishStatusMessageComposer = 2057,
CameraPurchaseOkMessageComposer = 2783,
CameraSnapshotMessageComposer = 463,
CameraStorageUrlMessageComposer = 3696,
CampaignCalendarDataMessageComposer = 2531,
CampaignCalendarDoorOpenedMessageComposer = 2551,
CanCreateRoomComposer = 378,
CanCreateRoomEventComposer = 2599,
CantConnectMessageComposer = 899,
CarryObjectMessageComposer = 1474,
CatalogPageExpirationComposer = 2668,
CatalogPageMessageComposer = 804,
CatalogPagesListComposer = 1032,
CatalogPageWithEarliestExpiryMessageComposer = 2515,
CatalogPublishedMessageComposer = 1866,
CategoriesWithVisitorCountComposer = 1455,
CfhChatlogComposer = 607,
CfhSanctionMessageComposer = 2782,
CfhTopicsInitComposer = 325,
ChangeEmailResultComposer = 1815,
ChangeUserNameResultMessageEvent = 118,
ChatMessageComposer = 1446,
ChatReviewSessionDetachedMessageComposer = 30,
ChatReviewSessionOfferedToGuideMessageComposer = 735,
ChatReviewSessionResultsMessageComposer = 3276,
ChatReviewSessionStartedMessageComposer = 143,
ChatReviewSessionVotingStatusMessageComposer = 1829,
CheckUserNameResultMessageComposer = 563,
CitizenshipVipOfferPromoEnabledComposer = 2278,
CloseConnectionMessageComposer = 122,
ClubGiftInfoComposer = 619,
ClubGiftNotificationComposer = 2188,
ClubGiftSelectedComposer = 659,
CollapsedCategoriesComposer = 1543,
CommunityGoalEarnedPrizesMessageComposer = 3319,
CommunityGoalHallOfFameMessageComposer = 3005,
CommunityGoalProgressMessageComposer = 2525,
CommunityGoalVoteMessageComposer = 1435,
CompetitionEntrySubmitResultComposer = 1177,
CompetitionRoomsDataMessageComposer = 3954,
CompetitionStatusMessageComposer = 133,
CompetitionVotingInfoMessageComposer = 3506,
CompleteDiffieHandshakeComposer = 3885,
ConcurrentUsersGoalProgressMessageComposer = 2737,
ConfirmBreedingRequestComposer = 634,
ConfirmBreedingResultComposer = 1625,
ConvertedRoomIdComposer = 1331,
CraftableProductsComposer = 1000,
CraftingRecipeComposer = 2774,
CraftingRecipesAvailableComposer = 2124,
CraftingResultComposer = 618,
CreditBalanceComposer = 3475,
CurrentTimingCodeMessageComposer = 1745,
CustomUserNotificationMessageComposer = 909,
DanceMessageComposer = 2233,
DiceValueMessageComposer = 3431,
DirectSmsClubBuyAvailableMessageComposer = 195,
DisconnectReasonComposer = 4000,
DoorbellMessageComposer = 2309,
ElementPointerMessageComposer = 1787,
EmailStatusResultComposer = 612,
EpicPopupMessageComposer = 3945,
ErrorReportComposer = 1004,
ExpressionMessageComposer = 1631,
ExtendedProfileChangedMessageComposer = 876,
ExtendedProfileMessageComposer = 3898,
FaqCategoriesMessageComposer = 2756,
FaqCategoryMessageComposer = 2819,
FaqClientFaqsMessageComposer = 2492,
FaqSearchResultsMessageComposer = 1551,
FaqTextMessageComposer = 3292,
FavoriteMembershipUpdateMessageComposer = 3403,
FavouriteChangedComposer = 2524,
FavouritesComposer = 151,
FigureSetIdsComposer = 1450,
FigureUpdateComposer = 2429,
FindFriendsProcessResultComposer = 1210,
FlatAccessDeniedMessageComposer = 878,
FlatAccessibleMessageComposer = 3783,
FlatControllerAddedComposer = 2088,
FlatControllerRemovedComposer = 1327,
FlatControllersComposer = 1284,
FlatCreatedComposer = 1304,
FloodControlMessageComposer = 566,
FloorHeightMapComposer = 1301,
FollowFriendFailedComposer = 3048,
ForumDataMessageComposer = 3011,
ForumsListMessageComposer = 3001,
FriendFurniCancelLockMessageComposer = 770,
FriendFurniOtherLockConfirmedMessageComposer = 382,
FriendFurniStartConfirmationMessageComposer = 3753,
FriendListFragmentMessageComposer = 3130,
FriendListUpdateComposer = 2800,
FriendNotificationComposer = 3082,
FriendRequestsComposer = 280,
FurniListAddOrUpdateComposer = 104,
FurniListComposer = 994,
FurniListInvalidateComposer = 3151,
FurniListRemoveComposer = 159,
FurniRentOrBuyoutOfferMessageComposer = 35,
FurnitureAliasesMessageComposer = 1723,
Game2AccountGameStatusMessageComposer = 2893,
Game2GameDirectoryStatusMessageComposer = 2246,
Game2InArenaQueueMessageComposer = 872,
Game2JoiningGameFailedMessageComposer = 1730,
Game2StopCounterMessageComposer = 2233,
Game2UserLeftGameMessageComposer = 2383,
Game2WeeklyFriendsLeaderboardComposer = 2270,
Game2WeeklyLeaderboardComposer = 2196,
GameAchievementsMessageComposer = 1689,
GameInviteMessageComposer = 904,
GameListMessageComposer = 222,
GamePlayerValueMessageComposer = 2324,
GameStatusMessageComposer = 3805,
GenericErrorComposer = 1600,
GetGuestRoomResultComposer = 687,
GiftReceiverNotFoundComposer = 1517,
GiftWrappingConfigurationComposer = 2234,
GotMysteryBoxPrizeMessageComposer = 3712,
GoToBreedingNestFailureComposer = 2621,
GroupDetailsChangedMessageComposer = 1459,
GroupMembershipRequestedMessageComposer = 1180,
GuestRoomSearchResultComposer = 52,
GuideOnDutyStatusMessageComposer = 1548,
GuideReportingStatusMessageComposer = 3463,
GuideSessionAttachedMessageComposer = 1591,
GuideSessionDetachedMessageComposer = 138,
GuideSessionEndedMessageComposer = 1456,
GuideSessionErrorMessageComposer = 673,
GuideSessionInvitedToGuideRoomMessageComposer = 219,
GuideSessionMessageMessageComposer = 841,
GuideSessionPartnerIsTypingComposer = 1016,
GuideSessionRequesterRoomMessageComposer = 1847,
GuideSessionStartedComposer = 3209,
GuideTicketCreationResultMessageComposer = 3285,
GuideTicketResolutionMessageComposer = 2674,
GuildCreatedMessageComposer = 2808,
GuildCreationInfoMessageComposer = 2159,
GuildEditFailedMessageComposer = 3988,
GuildEditInfoMessageComposer = 3965,
GuildEditorDataMessageComposer = 2238,
GuildForumThreadsComposer = 1073,
GuildFurniContextMenuInfoMessageComposer = 3293,
GuildMemberFurniCountInHqMessageComposer = 1876,
GuildMemberMgmtFailedMessageComposer = 818,
GuildMembersComposer = 1200,
GuildMembershipRejectedMessageComposer = 2445,
GuildMembershipsMessageComposer = 420,
GuildMembershipUpdatedMessageComposer = 265,
HabboAchievementNotificationMessageComposer = 806,
HabboActivityPointNotificationMessageComposer = 2275,
HabboBroadcastMessageComposer = 3801,
HabboClubExtendOfferMessageComposer = 3964,
HabboClubOffersMessageComposer = 2405,
HabboGroupBadgesMessageComposer = 2402,
HabboGroupDeactivatedMessageComposer = 3129,
HabboGroupDetailsMessageComposer = 1702,
HabboGroupJoinFailedMessageComposer = 762,
HabboSearchResultComposer = 973,
HabboWayQuizComposer1 = 3379,
HandItemReceivedMessageComposer = 354,
HeightMapComposer = 2753,
HeightMapUpdateMessageComposer = 558,
HotelClosedAndOpensComposer = 3728,
HotelClosesAndWillOpenAtComposer = 2771,
HotelMergeNameChangeComposer = 1663,
HotelViewCatalogPageExpiringComposer = 690,
HotelViewCustomTimerComposer = -1,
HotelWillCloseInMinutesComposer = 1050,
IdentityAccountsComposer = 3523,
IgnoredUsersMessageComposer = 126,
IgnoreResultMessageComposer = 207,
InClientLinkMessageComposer = 2023,
InfoFeedEnableMessageComposer = 3284,
InitCameraMessageComposer = 3878,
InitDiffieHandshakeComposer = 1347,
InstantMessageErrorComposer = 3359,
InterstitialMessageComposer = 1808,
IsBadgeRequestFulfilledComposer = 2998,
IsFirstLoginOfDayComposer = 793,
IsOfferGiftableMessageComposer = 761,
IssueCloseNotificationMessageComposer = 934,
IssueDeletedMessageComposer = 3192,
IssueInfoMessageComposer = 3609,
IssuePickFailedMessageComposer = 3150,
IsUserPartOfCompetitionMessageComposer = 3841,
ItemAddMessageComposer = 2187,
ItemDataUpdateMessageComposer = 2202,
ItemRemoveMessageComposer = 3208,
ItemsComposer = 1369,
ItemUpdateMessageComposer = 2009,
JoinedQueueMessageComposer = 2260,
JoiningQueueFailedMessageComposer = 3035,
JukeBoxPlaylistFullMessageComposer = 105,
JukeboxSongDisksMessageComposer = 34,
LatencyPingResponseMessageComposer = 10,
LeftQueueMessageComposer = 1477,
LimitedEditionSoldOutComposer = 377,
LimitedOfferAppearingNextMessageComposer = 44,
LoadGameMessageComposer = 3654,
LoadGameUrlMessageComposer = 2624,
MaintenanceStatusMessageComposer = 1350,
MarketplaceBuyOfferResultComposer = 2032,
MarketplaceCancelOfferResultComposer = 3264,
MarketplaceCanMakeOfferResult = 54,
MarketplaceConfigurationComposer = 1823,
MarketplaceItemStatsComposer = 725,
MarketplaceMakeOfferResult = 1359,
MarketplaceOffersComposer = 680,
MarketPlaceOwnOffersComposer = 3884,
MessengerErrorComposer = 892,
MessengerInitComposer = 1605,
MiniMailNewMessageComposer = 1911,
MiniMailUnreadCountComposer = 2803,
ModeratorActionResultMessageComposer = 2335,
ModeratorCautionComposer = 1890,
ModeratorInitMessageComposer = 2696,
ModeratorMessageComposer = 2030,
ModeratorRoomInfoComposer = 1333,
ModeratorToolPreferencesComposer = 1576,
ModeratorUserInfoComposer = 2866,
MOtdNotificationComposer = 2035,
MuteAllInRoomComposer = 2533,
MysteryBoxClosingComposer = 596,
MysteryBoxKeysMessageComposer = 2833,
MysteryBoxStartComposer = 3201,
NavigatorLiftedRoomsComposer = 3104,
NavigatorMetaDataComposer = 3052,
NavigatorSavedSearchesComposer = 3984,
NavigatorSearchResultBlocksComposer = 2690,
NavigatorSettingsComposer = 2875,
NestBreedingSuccessComposer = 2527,
NewConsoleMessageComposer = 1587,
NewFriendRequestComposer = 2219,
NewNavigatorPreferencesComposer = 518,
NewUserExperienceGiftOfferComposer = 3575,
NewUserExperienceNotCompleteComposer = 3639,
NoobnessLevelMessageComposer = 3738,
NoOwnedRoomsAlertMessageComposer = 2064,
NoSuchFlatComposer = 84,
NotEnoughBalanceMessageComposer = 3914,
NotificationDialogMessageComposer = 1992,
NowPlayingMessageComposer = 469,
ObjectAddMessageComposer = 1534,
ObjectDataUpdateMessageComposer = 2547,
ObjectRemoveMessageComposer = 2703,
ObjectsDataUpdateMessageComposer = 1453,
ObjectsMessageComposer = 1778,
ObjectUpdateMessageComposer = 3776,
OfferRewardDeliveredMessageComposer = 2125,
OfficialRoomsComposer = 2726,
OfficialSongIdMessageComposer = 1381,
OneWayDoorStatusMessageComposer = 2376,
OpenComposer = 1830,
OpenConnectionMessageComposer = 758,
OpenPetPackageRequestedMessageComposer = 2380,
OpenPetPackageResultMessageComposer = 546,
PerkAllowancesComposer = 2586,
PetAddedToInventoryComposer = 2101,
PetBreedingComposer = 1746,
PetBreedingResultComposer = 1553,
PetExperienceComposer = 2156,
PetFigureUpdateComposer = 1924,
PetInfoMessageComposer = 2901,
PetInventoryComposer = 3522,
PetLevelNotificationComposer = 859,
PetLevelUpdateComposer = 2824,
PetPlacingErrorComposer = 2913,
PetReceivedMessageComposer = 1111,
PetRemovedFromInventoryComposer = 3253,
PetRespectFailedComposer = 1130,
PetRespectNotificationComposer = 2788,
PetStatusUpdateComposer = 1907,
PetSupplementedNotificationComposer = 3441,
PetTrainingPanelComposer = 1164,
PhoneCollectionStateMessageComposer = 2890,
PingMessageComposer = 3928,
PlayListMessageComposer = 1748,
PlayListSongAddedMessageComposer = 1140,
PollContentsComposer = 2997,
PollErrorComposer = 662,
PollOfferComposer = 3785,
PopularRoomTagsResultComposer = 2012,
PostItPlacedComposer = 1501,
PostMessageMessageComposer = 2049,
PostThreadMessageComposer = 1862,
PresentOpenedMessageComposer = 56,
ProductOfferComposer = 3388,
PromoArticlesMessageComposer = 286,
PurchaseErrorMessageComposer = 1404,
PurchaseNotAllowedMessageComposer = 3770,
PurchaseOkMessageComposer = 869,
QuestCancelledMessageComposer = 3027,
QuestCompletedMessageComposer = 949,
QuestDailyMessageComposer = 1878,
QuestionAnsweredComposer = 2589,
QuestionComposer = 2665,
QuestionFinishedComposer = 1066,
QuestionInfoComposer = -1,
QuestMessageComposer = 230,
QuestsMessageComposer = 3625,
QuizDataMessageComposer = 2927,
QuizResultsMessageComposer = 2772,
RecyclerFinishedComposer = 468,
RecyclerPrizesComposer = 3164,
RecyclerStatusComposer = 3433,
RelationshipStatusInfoComposer = 2016,
RemainingMutePeriodComposer = 826,
RentableSpaceRentFailedMessageComposer = 1868,
RentableSpaceRentOkMessageComposer = 2046,
RentableSpaceStatusMessageComposer = 3559,
RequestSpamWallPostItMessageComposer = 2366,
RestoreClientMessageComposer = 426,
RoomAdErrorComposer = 1759,
RoomAdPurchaseInfoComposer = 2468,
RoomChatlogComposer = 3434,
RoomChatSettingsMessageComposer = 1191,
RoomDimmerPresetsComposer = 2710,
RoomEntryInfoComposer = -1,
RoomEntryInfoMessageComposer = 749,
RoomEntryTileMessageComposer = 1664,
RoomEventCancelComposer = 3479,
RoomEventComposer = 1840,
RoomFilterSettingsMessageComposer = 2937,
RoomFloorThicknessUpdatedComposer = 3786,
RoomForwardMessageComposer = 160,
RoomInfoUpdatedComposer = 3297,
RoomInviteComposer = 3870,
RoomInviteErrorComposer = 462,
RoomMessageNotificationMessageComposer = 1634,
RoomOccupiedTilesMessageComposer = 3990,
RoomPropertyMessageComposer = 2454,
RoomQueueStatusMessageComposer = 2208,
RoomRatingComposer = 482,
RoomReadyMessageComposer = 2031,
RoomSettingsDataComposer = 1498,
RoomSettingsErrorComposer = 2897,
RoomSettingsSavedComposer = 948,
RoomSettingsSaveErrorComposer = 1555,
RoomThumbnailUpdateResultComposer = 1927,
RoomUserQuestionAnsweredComposer = -1,
RoomUserRespectComposer = 2815,
RoomVisitsComposer = 1752,
RoomVisualizationSettingsComposer = 3547,
SanctionStatusComposer = 2221,
ScrSendKickbackInfoMessageComposer = 3277,
ScrSendUserInfoComposer = 954,
SeasonalCalendarDailyOfferMessageComposer = 1889,
SeasonalQuestsMessageComposer = 1122,
SecondsUntilMessageComposer = 3926,
SellablePetPalettesMessageComposer = 3331,
ShoutMessageComposer = 1036,
ShowEnforceRoomCategoryDialogComposer = 3896,
SleepMessageComposer = 1797,
SlideObjectBundleMessageComposer = 3207,
Str16258Composer = 1660,
Str16667Composer = 3099,
Str17054Composer = 416,
TalentLevelUpComposer = 638,
TalentTrackLevelMessageEvent = 1203,
TalentTrackMessageComposer = 3406,
TargetedOfferComposer = 119,
TargetedOfferNotFoundComposer = 1237,
ThreadMessagesMessageComposer = 509,
ThumbnailStatusMessageComposer = 3595,
TradeCloseWindowComposer = 1001,
TradeCompleteComposer = 2369,
TradingAcceptComposer = 2568,
TradingCloseComposer = 1373,
TradingConfirmationComposer = 2720,
TradingItemListComposer = 2024,
TradingNoSuchItemComposer = 2873,
TradingNotOpenComposer = 3128,
TradingOpenComposer = 2505,
TradingOpenFailedComposer = 217,
TradingOtherNotAllowedComposer = 1254,
TradingYouAreNotAllowedComposer = 3058,
TraxSongInfoMessageComposer = 3365,
TryPhoneNumberResultMessageComposer = 800,
TryVerificationCodeResultMessageComposer = 91,
UniqueMachineIdComposer = 1488,
Unknowncomposer1188 = 1437,
UnloadGameMessageComposer = 1715,
UnreadForumsCountMessageComposer = 2379,
UnseenItemsComposer = 2103,
UpdateMessageMessageComposer = 324,
UpdateStackHeightTileHeightComposer = 2816,
UpdateThreadMessageComposer = 2528,
UseObjectMessageComposer = 1774,
UserBadgesComposer = 1087,
UserBannedMessageComposer = 1683,
UserBcLimitsComposer = -1,
UserChangeMessageComposer = 3920,
UserChatlogComposer = 3377,
UserClassificationMessageComposer = 966,
UserEventCatsComposer = 3244,
UserFlatCatsComposer = 1562,
UserGameAchievementsMessageComposer = 2265,
UserNameChangedMessageComposer = 2182,
UserObjectComposer = 2725,
UserRemoveMessageComposer = 2661,
UserRightsMessageComposer = 411,
UsersComposer = 374,
UserSongDisksInventoryMessageComposer = 2602,
UserTagsMessageComposer = 1255,
UserTypingMessageComposer = 1717,
UserUnbannedFromRoomComposer = 3429,
UserUpdateComposer = 1640,
VoucherRedeemErrorMessageComposer = 714,
VoucherRedeemOkMessageComposer = 3336,
WardrobeMessageComposer = 3315,
WeeklyCompetitiveFriendsLeaderboardComposer = 3560,
WeeklyCompetitiveLeaderboardComposer = 3512,
WeeklyGameRewardComposer = 2641,
WeeklyGameRewardWinnersComposer = 3097,
WelcomeGiftChangeEmailResultComposer = 2293,
WelcomeGiftStatusComposer = 2707,
WhisperMessageComposer = 2704,
WiredConditionDataComposer = 1108,
WiredEffectDataComposer = 1434,
WiredRewardResultMessageComposer = 178,
WiredSavedComposer = 1155,
WiredTriggerDataComposer = 383,
WiredValidationErrorComposer = 156,
YouAreControllerMessageComposer = 780,
YouAreNotControllerMessageComposer = 2392,
YouAreOwnerMessageComposer = 339,
YouArePlayingGameMessageComposer = 448,
YouAreSpectatorMessageEvent = 1033,
YoutubeControlVideoMessageComposer = 1554,
YoutubeDisplayPlaylistsComposer = 1112,
YoutubeDisplayVideoMessageComposer = 1411,
SnowStormGameStartedComposer = 5000,
SnowStormQuePositionComposer = 5001,
SnowStormStartBlockTickerComposer = 5002,
SnowStormStartLobbyCounterComposer = 5003,
SnowStormUnusedAlertGenericComposer = 5004,
SnowStormLongDataComposer = 5005,
SnowStormGameEndedComposer = 5006,
SnowStormQuePlayerAddedComposer = 5008,
SnowStormPlayAgainComposer = 5009,
SnowStormGamesLeftComposer = 5010,
SnowStormQuePlayerRemovedComposer = 5011,
SnowStormGamesInformationComposer = 5012,
SnowStormLongData2Composer = 5013,
UnusedSnowstorm5014 = 5014,
SnowStormGameStatusComposer = 5015,
SnowStormFullGameStatusComposer = 5016,
SnowStormOnStageStartComposer = 5017,
SnowStormIntializeGameArenaViewComposer = 5018,
SnowStormRejoinPreviousRoomComposer = 5019,
UnknownSnowstorm5020 = 5020,
SnowStormLevelDataComposer = 5021,
SnowStormOnGameEndingComposer = 5022,
SnowStormUserChatMessageComposer = 5023,
SnowStormOnStageRunningComposer = 5024,
SnowStormOnStageEndingComposer = 5025,
SnowStormIntializedPlayersComposer = 5026,
SnowStormOnPlayerExitedArenaComposer = 5027,
SnowStormGenericErrorComposer = 5028,
SnowStormUserRematchedComposer = 5029
}

View File

@ -0,0 +1,70 @@
using System.Text;
using Tiger.Utils;
namespace Tiger.Communication.Messages.Types;
public class ClientMessage
{
private readonly byte[] _packet;
private int _readerIndex;
public short? Header { get; }
public ClientMessage(byte[] packet)
{
_packet = packet;
_readerIndex = 0;
Header = ReadInt16();
}
public short? ReadInt16()
{
var raw = ReadBytes(2);
if (raw.Length != 2) return null;
return ByteUtils.GetInt16(raw);
}
public int? ReadInt32()
{
var raw = ReadBytes(4);
if (raw.Length != 4) return null;
return ByteUtils.GetInt32(raw);
}
public bool? ReadBoolean()
{
if (_packet.Length - _readerIndex == 0) return null;
var result = _packet[_readerIndex];
_readerIndex++;
return result == 1;
}
public string? ReadString()
{
var strlen = ReadInt16();
if (strlen == null) return null;
var raw = ReadBytes(strlen.Value);
return raw.Length != strlen ? null : Encoding.UTF8.GetString(raw);
}
private byte[] ReadBytes(int amount)
{
if (_packet.Length - _readerIndex < amount)
{
return Array.Empty<byte>();
}
var data = new byte[amount];
Array.Copy(_packet, _readerIndex, data, 0, amount);
_readerIndex += amount;
return data;
}
}

View File

@ -0,0 +1,48 @@
using System.Text;
using Tiger.Utils;
namespace Tiger.Communication.Messages.Types;
public class ServerMessage
{
private readonly List<byte> _packet = new();
public ServerMessage(short header)
{
AppendInt16(header);
}
public void AppendInt16(short value)
{
_packet.AddRange(ByteUtils.Int16ToArray(value));
}
public void AppendInt32(int value)
{
_packet.AddRange(ByteUtils.Int32ToArray(value));
}
public void AppendUInt32(uint value)
{
_packet.AddRange(ByteUtils.UInt32ToArray(value));
}
public void AppendBoolean(bool value)
{
_packet.Add((byte)(value ? 1 : 0));
}
public void AppendString(string value)
{
AppendInt16((short)value.Length);
_packet.AddRange(Encoding.UTF8.GetBytes(value));
}
public byte[] ToArray()
{
var finalizedPacket = new List<byte>();
finalizedPacket.AddRange(ByteUtils.Int32ToArray(_packet.Count));
finalizedPacket.AddRange(_packet);
return finalizedPacket.ToArray();
}
}

39
Game/Habbos/Habbo.cs Normal file
View File

@ -0,0 +1,39 @@
using MySqlConnector;
namespace Tiger.Game.Habbos;
public class Habbo
{
public uint Id { get; }
public string Username { get; }
public string Email { get; }
public DateTime AccountCreated { get; }
public DateTime LastLogin { get; }
public string Motto { get; set; }
public string Figure { get; set; }
public string Gender { get; set; }
public byte Rank { get; set; }
public uint Credits { get; set; }
public bool Online { get; set; }
public uint HomeRoom { get; set; }
public uint AchievementScore { get; set; }
public uint GroupId { get; set; }
public Habbo(MySqlDataReader reader)
{
Id = reader.GetUInt32("id");
Username = reader.GetString("username");
Email = reader.GetString("email");
AccountCreated = reader.GetDateTime("account_created");
LastLogin = reader.GetDateTime("last_login");
Motto = reader.GetString("motto");
Figure = reader.GetString("figure");
Gender = reader.GetString("gender");
Rank = reader.GetByte("rank");
Credits = reader.GetUInt32("credits");
Online = reader.GetBoolean("online");
HomeRoom = reader.GetUInt32("home_room");
AchievementScore = reader.GetUInt32("achievement_score");
GroupId = reader.GetUInt32("group_id");
}
}

26
Game/Habbos/HabboDao.cs Normal file
View File

@ -0,0 +1,26 @@
using Tiger.Storage;
namespace Tiger.Game.Habbos;
public class HabboDao : IHabboDao
{
private readonly IDatabaseHelper _databaseHelper;
public HabboDao(IDatabaseHelper databaseHelper)
{
_databaseHelper = databaseHelper;
}
public async Task<Habbo?> GetHabboBySsoAsync(string sso)
{
await using var dbConnection = _databaseHelper.GetConnection();
var resultSet =
await dbConnection.GetResultSet("SELECT * FROM habbos WHERE sso_ticket = @sso LIMIT 1", ("@sso", sso));
if (await resultSet.ReadAsync())
{
return new Habbo(resultSet);
}
return null;
}
}

6
Game/Habbos/IHabboDao.cs Normal file
View File

@ -0,0 +1,6 @@
namespace Tiger.Game.Habbos;
public interface IHabboDao
{
Task<Habbo?> GetHabboBySsoAsync(string sso);
}

View File

@ -0,0 +1,6 @@
namespace Tiger.Game.Settings;
public interface ISettingsDao
{
Task<IReadOnlyDictionary<string, object>> GetSettingsAsync();
}

View File

@ -0,0 +1,7 @@
namespace Tiger.Game.Settings;
public interface ISettingsManager
{
Task ReloadSettingsAsync();
T GetSetting<T>(string key);
}

View File

@ -0,0 +1,27 @@
using System.Collections.ObjectModel;
using Tiger.Storage;
namespace Tiger.Game.Settings;
public class SettingsDao : ISettingsDao
{
private readonly IDatabaseHelper _databaseHelper;
public SettingsDao(IDatabaseHelper databaseHelper)
{
_databaseHelper = databaseHelper;
}
public async Task<IReadOnlyDictionary<string, object>> GetSettingsAsync()
{
var settings = new Dictionary<string, object>();
await using var dbConnection = _databaseHelper.GetConnection();
await using var resultSet = await dbConnection.GetResultSet("SELECT * FROM settings");
while (await resultSet.ReadAsync())
{
settings.Add(resultSet.GetString("skey"), resultSet.GetValue(resultSet.GetOrdinal("value")));
}
return new ReadOnlyDictionary<string, object>(settings);
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.Extensions.Logging;
namespace Tiger.Game.Settings;
public class SettingsManager : ISettingsManager
{
private IReadOnlyDictionary<string, object> _settings;
private readonly ISettingsDao _settingsDao;
private readonly ILogger<ISettingsManager> _logger;
public SettingsManager(ISettingsDao settingsDao, ILogger<ISettingsManager> logger)
{
_settingsDao = settingsDao;
_logger = logger;
_settings = new Dictionary<string, object>();
}
public async Task ReloadSettingsAsync()
{
_settings = await _settingsDao.GetSettingsAsync();
_logger.LogInformation("Loaded {Count} settings", _settings.Count);
}
public T GetSetting<T>(string key)
{
if (!_settings.TryGetValue(key, out var value))
throw new KeyNotFoundException($"No setting found with key '{key}'");
if (value is T castedValue)
return castedValue;
throw new InvalidCastException($"Setting '{key}' is not of type '{typeof(T)}'");
}
}

View File

@ -0,0 +1,6 @@
namespace Tiger.Networking.Game;
public interface IWebSocketServer
{
Task Start(string uriPrefix);
}

View File

@ -0,0 +1,28 @@
using System.Net.WebSockets;
using Tiger.Communication.Messages.Interfaces;
using Tiger.Communication.Messages.Types;
using Tiger.Game.Habbos;
namespace Tiger.Networking.Game.Sessions;
public class GameSession
{
public WebSocket WebSocket { get; }
public string SessionId { get; }
public Habbo? Habbo { get; set; }
public GameSession(WebSocket webSocket, string sessionId)
{
WebSocket = webSocket;
SessionId = sessionId;
}
public async Task SendComposerAsync(IMessageComposer composer)
{
var message = new ServerMessage((short)composer.Header);
composer.Compose(message);
var bytes = message.ToArray();
await WebSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true,
CancellationToken.None);
}
}

View File

@ -0,0 +1,56 @@
using System.Collections.Concurrent;
using System.Net.WebSockets;
namespace Tiger.Networking.Game.Sessions;
public class GameSessionManager : IGameSessionManager
{
private readonly ConcurrentDictionary<string, GameSession> _sessions = new();
public GameSession AddSession(WebSocket webSocket)
{
var sessionId = Guid.NewGuid().ToString();
var gameSession = new GameSession(webSocket, sessionId);
_sessions.TryAdd(sessionId, gameSession);
return gameSession;
}
public void RemoveSession(WebSocket webSocket)
{
var session = GetSession(webSocket);
if (session != null)
{
_sessions.TryRemove(session.SessionId, out _);
}
}
public void CloseAll()
{
foreach (var session in _sessions.Values)
{
session.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server shutdown", CancellationToken.None);
}
}
public GameSession? GetSession(WebSocket webSocket)
{
return (from session in _sessions where session.Value.WebSocket == webSocket select session.Value).FirstOrDefault();
}
public GameSession? GetSession(string sessionId)
{
return _sessions.TryGetValue(sessionId, out var gameSession) ? gameSession : null;
}
public async Task CloseAsync(string reason, GameSession session)
{
if (_sessions.TryGetValue(session.SessionId, out var gameSession))
{
if (gameSession.WebSocket.State == WebSocketState.Open)
{
await gameSession.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, reason, CancellationToken.None);
}
_sessions.TryRemove(session.SessionId, out _);
}
}
}

View File

@ -0,0 +1,13 @@
using System.Net.WebSockets;
namespace Tiger.Networking.Game.Sessions;
public interface IGameSessionManager
{
GameSession AddSession(WebSocket webSocket);
void RemoveSession(WebSocket webSocket);
void CloseAll();
GameSession? GetSession(WebSocket webSocket);
GameSession? GetSession(string sessionId);
Task CloseAsync(string reason, GameSession gameSession);
}

View File

@ -0,0 +1,104 @@
using System.Net;
using System.Net.WebSockets;
using Tiger.Communication.Messages;
using Tiger.Communication.Messages.Types;
using Tiger.Networking.Game.Sessions;
using Tiger.Utils;
namespace Tiger.Networking.Game;
public class WebSocketServer : IWebSocketServer
{
private readonly HttpListener _httpListener = new();
private readonly IGameSessionManager _gameSessionManager;
private readonly IMessageHandler _messageHandler;
public WebSocketServer(IGameSessionManager gameSessionManager, IMessageHandler messageHandler)
{
_gameSessionManager = gameSessionManager;
_messageHandler = messageHandler;
}
public async Task Start(string uriPrefix)
{
_httpListener.Prefixes.Add(uriPrefix);
_httpListener.Start();
Console.WriteLine($"Listening on {uriPrefix}...");
while (true)
{
var context = await _httpListener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
ProcessRequestAsync(context);
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
}
private async void ProcessRequestAsync(HttpListenerContext context)
{
WebSocket? webSocket = null;
try
{
var webSocketContext = await context.AcceptWebSocketAsync(null);
webSocket = webSocketContext.WebSocket;
var session = _gameSessionManager.AddSession(webSocket);
Console.WriteLine($"WebSocket Session {session.SessionId} has connected.");
await ReceiveMessageAsync(session.SessionId);
}
catch (Exception e)
{
Console.WriteLine($"WebSocket Session Error: {e.Message}");
}
finally
{
if (webSocket != null)
{
_gameSessionManager.RemoveSession(webSocket);
webSocket.Dispose();
}
}
}
private async Task ReceiveMessageAsync(string sessionId)
{
var buffer = new byte[1024 * 4];
while (_gameSessionManager.GetSession(sessionId) is { WebSocket.State: WebSocketState.Open } gameSession)
{
var result = await gameSession.WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
var offset = 0;
while (result.MessageType == WebSocketMessageType.Binary && offset + 4 <= result.Count)
{
var length = ByteUtils.GetInt32(new[]
{
buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3]
});
offset += 4;
if (offset + length > result.Count) break;
var packet = new byte[result.Count];
Array.Copy(buffer, offset, packet, 0, length);
offset += length;
_messageHandler.TryHandleAsync(gameSession, new ClientMessage(packet));
}
if (result.MessageType == WebSocketMessageType.Close)
{
await gameSession.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
}
}
}
}

44
Program.cs Normal file
View File

@ -0,0 +1,44 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Yaml;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Tiger.Communication.Messages;
using Tiger.Game.Habbos;
using Tiger.Game.Settings;
using Tiger.Networking.Game;
using Tiger.Networking.Game.Sessions;
using Tiger.Storage;
using Tiger.Utils;
var quitEvent = new ManualResetEvent(false);
Console.CancelKeyPress += (sender, e) =>
{
quitEvent.Set();
e.Cancel = true;
};
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddYamlFile("appsettings.yaml")
.Build();
var collection = new ServiceCollection();
collection.AddLogging(builder => builder.AddConsole());
collection.AddSingleton<IConfiguration>(configuration);
collection.AddSingleton<IWebSocketServer, WebSocketServer>();
collection.AddSingleton<IGameSessionManager, GameSessionManager>();
collection.AddSingleton<IMessageHandler, MessageHandler>();
collection.AddSingleton<IDatabaseHelper, DatabaseHelper>();
collection.AddSingleton<ISettingsDao, SettingsDao>();
collection.AddSingleton<ISettingsManager, SettingsManager>();
collection.AddSingleton<IHabboDao, HabboDao>();
collection.RegisterMessageEvents();
var provider = collection.BuildServiceProvider();
await provider.GetRequiredService<ISettingsManager>().ReloadSettingsAsync();
provider.GetRequiredService<IWebSocketServer>().Start($"http://{configuration["Network:Game:Ip"]}:{configuration["Network:Game:Port"]}/");
quitEvent.WaitOne();

View File

@ -0,0 +1,38 @@
using MySqlConnector;
namespace Tiger.Storage;
public class DatabaseConnection : IAsyncDisposable
{
private readonly MySqlConnection _connection;
private readonly MySqlCommand _command;
public DatabaseConnection(MySqlConnection connection)
{
_connection = connection;
_command = connection.CreateCommand();
}
public async Task<MySqlDataReader> GetResultSet(string query, params (string, object)[] mysqlParams)
{
await _connection.OpenAsync();
_command.CommandText = query;
foreach (var mysqlParam in mysqlParams)
{
_command.Parameters.AddWithValue(mysqlParam.Item1, mysqlParam.Item2);
}
var reader = await _command.ExecuteReaderAsync();
_command.Parameters.Clear();
return reader;
}
public async ValueTask DisposeAsync()
{
await _connection.DisposeAsync();
await _command.DisposeAsync();
GC.SuppressFinalize(this);
}
}

31
Storage/DatabaseHelper.cs Normal file
View File

@ -0,0 +1,31 @@
using Microsoft.Extensions.Configuration;
using MySqlConnector;
namespace Tiger.Storage;
public class DatabaseHelper : IDatabaseHelper
{
private readonly string _connectionString;
public DatabaseHelper(IConfiguration configuration)
{
var stringBuilder = new MySqlConnectionStringBuilder
{
Server = configuration["Database:Host"],
Port = uint.Parse(configuration["Database:Port"] ?? "3306"),
UserID = configuration["Database:Username"],
Password = configuration["Database:Password"],
Database = configuration["Database:Database"],
Pooling = true,
MinimumPoolSize = uint.Parse(configuration["Database:MinPool"] ?? "5"),
MaximumPoolSize = uint.Parse(configuration["Database:MaxPool"] ?? "15")
};
_connectionString = stringBuilder.ToString();
}
public DatabaseConnection GetConnection()
{
return new DatabaseConnection(new MySqlConnection(_connectionString));
}
}

View File

@ -0,0 +1,6 @@
namespace Tiger.Storage;
public interface IDatabaseHelper
{
DatabaseConnection GetConnection();
}

27
TigerEmu.csproj Normal file
View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Tiger</RootNamespace>
<LangVersion>11</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0-rc.1.23419.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.0-rc.1.23419.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Yaml" Version="2.0.0-preview2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0-rc.1.23419.4" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0-rc.1.23419.4" />
<PackageReference Include="MySqlConnector" Version="2.3.0-beta.3" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

29
Utils/ByteUtils.cs Normal file
View File

@ -0,0 +1,29 @@
namespace Tiger.Utils;
public static class ByteUtils
{
public static int GetInt32(byte[] array)
{
return (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3];
}
public static short GetInt16(byte[] array)
{
return (short)((array[0] << 8) | array[1]);
}
public static IEnumerable<byte> UInt32ToArray(uint value)
{
return new[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value };
}
public static IEnumerable<byte> Int32ToArray(int value)
{
return new[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value };
}
public static IEnumerable<byte> Int16ToArray(short value)
{
return new[] { (byte)(value >> 8), (byte)value };
}
}

View File

@ -0,0 +1,25 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Tiger.Communication.Messages.Interfaces;
namespace Tiger.Utils;
public static class DependencyInjectionExtensions
{
public static void RegisterMessageEvents(this IServiceCollection services)
{
// Get the assembly you are interested in (this could be any Assembly)
var assembly = Assembly.GetExecutingAssembly();
// Get all types that implement IMessageEvent and are not abstract
var messageEventTypes = assembly.GetTypes()
.Where(t => t.GetInterfaces().Contains(typeof(IMessageEvent)) && !t.IsAbstract);
// Register each type with AddSingleton
foreach (var type in messageEventTypes)
{
services.AddSingleton(typeof(IMessageEvent), type);
}
}
}

12
appsettings.yaml Normal file
View File

@ -0,0 +1,12 @@
Database:
Host: 127.0.0.1
Port: 3306
Username: root
Password: 123
Database: tiger_db
MinPool: 5
MaxPool: 15
Network:
Game:
Ip: 127.0.0.1
Port: 2096