2023-10-09 21:52:00 +02:00
|
|
|
|
// <copyright file="ConnectionManager.ChatServer.Custom.cs" company="MUnique">
|
|
|
|
|
|
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
|
|
|
|
|
// </copyright>
|
|
|
|
|
|
|
2024-11-19 07:28:51 +01:00
|
|
|
|
namespace MUnique.Client.Library;
|
2023-10-09 21:52:00 +02:00
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using MUnique.OpenMU.Network;
|
|
|
|
|
|
using MUnique.OpenMU.Network.Packets.ChatServer;
|
|
|
|
|
|
using MUnique.OpenMU.Network.Xor;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Extension methods to start writing messages of this namespace on a <see cref="IConnection"/>.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class ConnectionManager
|
|
|
|
|
|
{
|
|
|
|
|
|
private const int TokenOffset = 6;
|
|
|
|
|
|
private const int MessageOffset = 5;
|
|
|
|
|
|
|
|
|
|
|
|
private static readonly Xor3Encryptor TokenEncryptor = new(TokenOffset);
|
|
|
|
|
|
private static readonly Xor3Encryptor MessageEncryptor = new(MessageOffset);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sends a <see cref="Authenticate" /> to this connection.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="handle">The handle of the connection.</param>
|
|
|
|
|
|
/// <param name="roomId">The room id.</param>
|
|
|
|
|
|
/// <param name="token">The token to authenticate the client.</param>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// Is sent by the client when: This packet is sent by the client after it connected to the server, to authenticate itself.
|
|
|
|
|
|
/// Causes reaction on server side: The server will check the token. If it's correct, the client gets added to the requested chat room.
|
|
|
|
|
|
/// </remarks>
|
2024-11-19 07:28:51 +01:00
|
|
|
|
[UnmanagedCallersOnly(EntryPoint = "ConnectionManager_SendAuthenticateExt")]
|
2023-10-09 21:52:00 +02:00
|
|
|
|
public static void SendAuthenticateExt(int handle, ushort @roomId, uint token)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Connections.TryGetValue(handle, out var connection))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
connection.CreateAndSend(pipeWriter =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var length = AuthenticateRef.Length;
|
|
|
|
|
|
var packet = new AuthenticateRef(pipeWriter.GetSpan(length)[..length]);
|
|
|
|
|
|
packet.RoomId = @roomId;
|
|
|
|
|
|
|
|
|
|
|
|
var tokenString = token.ToString();
|
|
|
|
|
|
Encoding.UTF8.GetBytes(tokenString, packet.Token);
|
|
|
|
|
|
TokenEncryptor.Encrypt(packet);
|
|
|
|
|
|
|
|
|
|
|
|
return length;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// Log exception
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sends a <see cref="ChatMessage" /> to this connection.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="handle">The handle of the connection.</param>
|
|
|
|
|
|
/// <param name="senderIndex">The sender index.</param>
|
|
|
|
|
|
/// <param name="message">The message. It's "encrypted" with the 3-byte XOR key (FC CF AB).</param>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// Is sent by the server when: This packet is sent by the server after another chat client sent a message to the current chat room.
|
|
|
|
|
|
/// Causes reaction on client side: The client will show the message.
|
|
|
|
|
|
/// </remarks>
|
2024-11-19 07:28:51 +01:00
|
|
|
|
[UnmanagedCallersOnly(EntryPoint = "ConnectionManager_SendChatMessageExt")]
|
2023-10-09 21:52:00 +02:00
|
|
|
|
public static void SendChatMessageExt(int handle, byte @senderIndex, IntPtr @message)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Connections.TryGetValue(handle, out var connection))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
connection.CreateAndSend(pipeWriter =>
|
|
|
|
|
|
{
|
Run and connect the client natively on Linux (issue #462 Phase 4)
The engine now boots into the login scene, renders via SDL/OpenGL, and
connects to a server on Linux. Brings up the remaining runtime pieces and
fixes the bugs that surfaced once the game actually ran.
Networking (.NET client library on Linux):
- Build MUnique.Client.Library as a native linux-x64 Native AOT shared
object and dlopen the .so instead of the Windows .dll.
- Fix the native-to-managed string marshalling: the client passes wchar_t*,
which is 4-byte UTF-32 on Linux but the library decoded it as UTF-16,
garbling the server address into a bogus IP so connect() hung forever on
the main thread (the apparent "black screen"). Decode by the platform's
real wchar_t width. Updates the XSLT template so regeneration stays correct.
Runtime fixes uncovered by running:
- Config was never read on Linux: the exe-dir split looked for a backslash
only, so config.ini was never found and every setting fell back to its
default. Accept either path separator.
- UI piled into the top-left: CInput::Create bailed on the null SDL window
handle before setting the screen size, so centered windows computed
negative positions. Set the size regardless of the handle off Windows.
- LP64 width bugs reading binary assets (size fields are 32-bit on disk, but
long/size_t are 8 bytes on Linux x64): pet.bmd speed array, BMD encrypted
size, camera walk-script waypoint count.
- Double fclose() of the same handle (filter file, object files): tolerated
on the old CRT, a double-free on glibc.
- mu_swprintf bounds the buffer by its real array size to stop a fortify
abort, and the GL frame can be dumped via MU_CAPTURE_FRAME for headless
verification.
Portable subsystems:
- GDI text backend backed by stb_truetype (CreateFont/CreateDIBSection/
TextOut), so UI text renders without Win32 GDI.
- Case-correcting path resolver so Windows-style asset paths resolve on a
case-sensitive filesystem, wired through every file-open shim.
- GameShop compiles on Linux: the UI and list managers build, only the
WinINet/urlmon downloader is excluded, with the download entry points
failing gracefully.
- Portable shims extended: StrSafe, Sleep/GetLastError, file attribute and
directory helpers, system-info for the crash log, and the Linux entry point
now calls the shared WinMain bootstrap.
2026-06-13 18:22:03 +02:00
|
|
|
|
var messageString = NativeInterop.PtrToWideString(@message);
|
2023-10-09 21:52:00 +02:00
|
|
|
|
var messageLength = Encoding.UTF8.GetByteCount(messageString!);
|
|
|
|
|
|
|
|
|
|
|
|
var length = ChatMessageRef.GetRequiredSize(messageLength);
|
|
|
|
|
|
var packet = new ChatMessageRef(pipeWriter.GetSpan(length)[..length]);
|
|
|
|
|
|
packet.SenderIndex = @senderIndex;
|
|
|
|
|
|
packet.MessageLength = (byte)messageLength;
|
|
|
|
|
|
Encoding.UTF8.GetBytes(messageString, packet.Message);
|
|
|
|
|
|
MessageEncryptor.Encrypt(packet);
|
|
|
|
|
|
|
|
|
|
|
|
return length;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// Log exception
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|