Helios
Recreating the 2013 experience of Habbo Hotel
This is the development thread for Helios, a private Habbo Hotel server designed to emulate the 2013 version of Habbo using C# and .NET Core. The server currently uses the SWF release RELEASE63-201302211227-193109692, but it may be updated in the future. It has been in development in 2020, but I've been going off/on from it, development time spent on it hasn't been consistent, but I am to speed up development a lot this year.Recreating the 2013 experience of Habbo Hotel
Helios utilises DotNetty for asynchronous TCP sockets and networking. It also make use of Entity Framework for convenient database access and SQL queries without the need for manual query writing. In addition, we use Newtonsoft.Json for efficient JSON serializing and deserializing of various custom item attributes. This has been written from scratch and is all my work, it is not based off any existing source code.
This project comes in two, there's the server and there's also the website. The website is written in ASP.NET Core and will be recreating what Habbo Hotel was like back in 2013 - there's not much difference from what you remember of PHPRetro, but some notable changes were the multiple avatars per user, the quick registration, and the new login page.
The reason why I'm doing 2013 is because releases at the time did not do a very good job faithfully emulating this version, if you've heard of SwiftEmu/BcStorm then you will know what I'm talking about. I am to fully correct this and recreate this version to the best of my ability.
Source Code
The source code will be available eventually at the GitHub organisation released under GPL v3:
Project Helios
This is the organisation page for the many repositories for Helios, a Habbo Hotel server written in C# .NET 6 built for the 2013+ release of Habbo. - Project Helios
github.com
There will be three repositories, all using .NET 6.
- Helios.Server
- DotNetty
- Helios.Web
- ASP.NET Core
- Helios.Storage
- Entity Framework Core
Features
User
- User login with SSO including SSO expiry
- User logout
- User information retrieval
- Seasonal currency support
- Add friend
- Remove friend
- Send friend request
- Remove friend request
- Accept friend request
- Instant message friend
- Update friend status when logging in/out/entering rooms
- Follow friend
- Promotable room display supported
- Random promotion to display on other navigator tabs
- Enter room
- Walk in room
- Leave room
- Multiple user support in room
- Chatting in room
- Item walking collision
- Allow walkthrough user setting (can't save settings yet)
- Catalogue discounts
- Purchase items (including bulk purchase and discount calculation)
- Trophy purchase supported
- Post-its supported
- Seasonal currency supported
- Place floor items
- Place wall items
- Move floor items
- Move wall items
- Pick up floor items
- Pickup wall items
- Purchase Habbo Club
- Renew Habbo Club
- Habbo Club Gift system (relies on MySQL scheduler!)
- Stickies/Post-Its
- Trophies
- Rollers
- Teleporters
- Mannequins
Code Snippets
C#:
namespace Helios.Game
{
public class StickieInteractor : Interactor
{
#region Overridden Properties
public override ExtraDataType ExtraDataType => ExtraDataType.StringData;
#endregion
public StickieInteractor(Item item) : base(item) { }
public override object GetJsonObject()
{
StickieExtraData extraData = null;
try
{
extraData = JsonConvert.DeserializeObject<StickieExtraData>(Item.Data.ExtraData);
} catch { }
if (extraData == null)
{
extraData = new StickieExtraData
{
Message = string.Empty,
Colour = "FFFF33"
};
}
return extraData;
}
public override object GetExtraData(bool inventoryView = false)
{
if (NeedsExtraDataUpdate)
{
NeedsExtraDataUpdate = false;
ExtraData = ((StickieExtraData)GetJsonObject()).Colour;
}
return ExtraData;
}
}
}
C#:
using Helios.Game;
using Helios.Network.Streams;
using Helios.Storage.Database.Access;
using Helios.Storage.Database.Data;
namespace Helios.Messages.Incoming
{
public class AcceptRequestsMessageEvent : IMessageEvent
{
public void Handle(Player player, Request request)
{
int friendsAccepted = request.ReadInt();
var messenger = player.Messenger;
for (int i = 0; i < friendsAccepted; i++)
{
int userId = request.ReadInt();
if (!messenger.HasRequest(userId))
continue;
if (messenger.Friends.Count >= messenger.MaxFriendsAllowed)
continue;
var playerData = PlayerManager.Instance.GetDataById(userId);
if (playerData == null)
continue;
var targetMessenger = Messenger.GetMessengerData(userId);
var targetFriend = new MessengerUser(playerData);
targetMessenger.Friends.Add(messenger.MessengerUser);
messenger.Friends.Add(targetFriend);
targetMessenger.RemoveRequest(player.Details.Id);
messenger.RemoveRequest(userId);
var targetPlayer = PlayerManager.Instance.GetPlayerById(userId);
if (targetPlayer != null)
{
targetPlayer.Messenger.QueueUpdate(MessengerUpdateType.AddFriend, messenger.MessengerUser);
targetPlayer.Messenger.ForceUpdate();
}
MessengerDao.DeleteRequests(player.Details.Id, userId);
MessengerDao.SaveFriend(new MessengerFriendData
{
FriendId = userId,
UserId = player.Details.Id
});
MessengerDao.SaveFriend(new MessengerFriendData
{
UserId = userId,
FriendId = player.Details.Id
});
messenger.QueueUpdate(MessengerUpdateType.AddFriend, targetFriend);
}
messenger.ForceUpdate();
}
}
}
Images
Website
Progress on the website has been slow, because I've been browsing old projects and repositories to get the HTML and steps exactly the same as what it once was. I've also been using YouTube videos and Habborator etc for first-hand sources of what 2013 was like, to get an accurate depiction and recreation. Anything I can't exactly recreate 1:1 - I've been faithful with my changes.
Features
Homepage
- Login page
Register
- Quick register steps
- Suggest random but appropriate user figures
- Input validation
Code Snippets
C#:
[Route("/quickregister/step2")]
public IActionResult Step2()
{
if (Request.Query.ContainsKey("p") &&
Request.Query["p"] == "register")
{
return RedirectToAction("Start");
}
if (TempData.ContainsKey("Error"))
ViewBag.Error = TempData["Error"];
if (!HttpContext.Contains("registerYear") ||
!HttpContext.Contains("registerMonth") ||
!HttpContext.Contains("registerDay") ||
!HttpContext.Contains("registerGender"))
{
TempData["Error"] = "fields";
return RedirectToAction("Start");
}
return View("Step2");
}
Images
Last edited: