mumain/ClientLibrary/ConnectionManager.ChatServerFunctions.cs

245 lines
9.1 KiB
C#
Raw Permalink Normal View History

// <copyright file="ConnectionManager.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
//------------------------------------------------------------------------------
// <auto-generated>
// This source code was auto-generated by an XSL transformation.
// Do not change this file. Instead, change the XML data which contains
// the packet definitions and re-run the transformation (publish/rebuild the
// client library project).
// </auto-generated>
//------------------------------------------------------------------------------
// ReSharper disable RedundantVerbatimPrefix
// ReSharper disable AssignmentIsFullyDiscarded
// ReSharper disable UnusedMember.Global
// ReSharper disable UseObjectOrCollectionInitializer
#nullable enable
namespace MUnique.Client.Library;
using System;
2026-01-04 13:53:58 +01:00
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using MUnique.OpenMU.Network;
using MUnique.OpenMU.Network.Packets;
using MUnique.OpenMU.Network.Packets.ChatServer;
/// <summary>
/// Extension methods to start writing messages of this namespace on a <see cref="IConnection"/>.
/// </summary>
public unsafe partial class ConnectionManager
{
/// <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">A token (integer number), formatted as string and "encrypted" with the 3-byte XOR key (FC CF AB).</param>
/// <param name="tokenByteLength">The length of <paramref name="token"/>.</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>
[UnmanagedCallersOnly(EntryPoint = "SendAuthenticate")]
public static void SendAuthenticate(int handle, ushort @roomId, byte* @token, uint tokenByteLength)
{
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;
new Span<byte>(@token, (int)tokenByteLength).CopyTo(packet.Token);
return length;
});
}
2026-01-04 13:53:58 +01:00
catch (Exception ex)
{
2026-01-04 13:53:58 +01:00
Debug.WriteLine(ex);
}
}
/// <summary>
/// Sends a <see cref="ChatRoomClientJoined" /> to this connection.
/// </summary>
/// <param name="handle">The handle of the connection.</param>
/// <param name="clientIndex">The client index.</param>
/// <param name="name">The name.</param>
/// <remarks>
/// Is sent by the server when: This packet is sent by the server after another chat client joined the chat room.
/// Causes reaction on client side: The client will add the client in its list (if over 2 clients are connected to the same room), or show its name in the title bar.
/// </remarks>
[UnmanagedCallersOnly(EntryPoint = "SendChatRoomClientJoined")]
public static void SendChatRoomClientJoined(int handle, byte @clientIndex, IntPtr @name)
{
if (!Connections.TryGetValue(handle, out var connection))
{
return;
}
try
{
connection.CreateAndSend(pipeWriter =>
{
var length = ChatRoomClientJoinedRef.Length;
var packet = new ChatRoomClientJoinedRef(pipeWriter.GetSpan(length)[..length]);
packet.ClientIndex = @clientIndex;
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
packet.Name = NativeInterop.PtrToWideString(@name);
return length;
});
}
2026-01-04 13:53:58 +01:00
catch (Exception ex)
{
2026-01-04 13:53:58 +01:00
Debug.WriteLine(ex);
}
}
/// <summary>
/// Sends a <see cref="LeaveChatRoom" /> to this connection.
/// </summary>
/// <param name="handle">The handle of the connection.</param>
/// <remarks>
/// Is sent by the client when: This packet is sent by the client when it leaves the chat room, before the connection closes.
/// Causes reaction on server side: The server will remove the client from the chat room, notifying the remaining clients.
/// </remarks>
[UnmanagedCallersOnly(EntryPoint = "SendLeaveChatRoom")]
public static void SendLeaveChatRoom(int handle)
{
if (!Connections.TryGetValue(handle, out var connection))
{
return;
}
try
{
connection.CreateAndSend(pipeWriter =>
{
var length = LeaveChatRoomRef.Length;
var packet = new LeaveChatRoomRef(pipeWriter.GetSpan(length)[..length]);
return length;
});
}
2026-01-04 13:53:58 +01:00
catch (Exception ex)
{
2026-01-04 13:53:58 +01:00
Debug.WriteLine(ex);
}
}
/// <summary>
/// Sends a <see cref="ChatRoomClientLeft" /> to this connection.
/// </summary>
/// <param name="handle">The handle of the connection.</param>
/// <param name="clientIndex">The client index.</param>
/// <param name="name">The name.</param>
/// <remarks>
/// Is sent by the server when: This packet is sent by the server after a chat client left the chat room.
/// Causes reaction on client side: The client will remove the client from its list, or mark its name in the title bar as offline.
/// </remarks>
[UnmanagedCallersOnly(EntryPoint = "SendChatRoomClientLeft")]
public static void SendChatRoomClientLeft(int handle, byte @clientIndex, IntPtr @name)
{
if (!Connections.TryGetValue(handle, out var connection))
{
return;
}
try
{
connection.CreateAndSend(pipeWriter =>
{
var length = ChatRoomClientLeftRef.Length;
var packet = new ChatRoomClientLeftRef(pipeWriter.GetSpan(length)[..length]);
packet.ClientIndex = @clientIndex;
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
packet.Name = NativeInterop.PtrToWideString(@name);
return length;
});
}
2026-01-04 13:53:58 +01:00
catch (Exception ex)
{
2026-01-04 13:53:58 +01:00
Debug.WriteLine(ex);
}
}
/// <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="messageLength">The message length.</param>
/// <param name="message">The message. It's "encrypted" with the 3-byte XOR key (FC CF AB).</param>
/// <param name="messageByteLength">The length of <paramref name="message"/>.</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>
[UnmanagedCallersOnly(EntryPoint = "SendChatMessage")]
public static void SendChatMessage(int handle, byte @senderIndex, byte @messageLength, byte* @message, uint messageByteLength)
{
if (!Connections.TryGetValue(handle, out var connection))
{
return;
}
try
{
connection.CreateAndSend(pipeWriter =>
{
var length = ChatMessageRef.GetRequiredSize((int)messageByteLength);
var packet = new ChatMessageRef(pipeWriter.GetSpan(length)[..length]);
packet.SenderIndex = @senderIndex;
packet.MessageLength = @messageLength;
new Span<byte>(@message, (int)messageByteLength).CopyTo(packet.Message);
return length;
});
}
2026-01-04 13:53:58 +01:00
catch (Exception ex)
{
2026-01-04 13:53:58 +01:00
Debug.WriteLine(ex);
}
}
/// <summary>
/// Sends a <see cref="KeepAlive" /> to this connection.
/// </summary>
/// <param name="handle">The handle of the connection.</param>
/// <remarks>
/// Is sent by the client when: This packet is sent by the client in a fixed interval.
/// Causes reaction on server side: The server will keep the connection and chat room intact as long as the clients sends a message in a certain period of time.
/// </remarks>
[UnmanagedCallersOnly(EntryPoint = "SendKeepAlive")]
public static void SendKeepAlive(int handle)
{
if (!Connections.TryGetValue(handle, out var connection))
{
return;
}
try
{
connection.CreateAndSend(pipeWriter =>
{
var length = KeepAliveRef.Length;
var packet = new KeepAliveRef(pipeWriter.GetSpan(length)[..length]);
return length;
});
}
2026-01-04 13:53:58 +01:00
catch (Exception ex)
{
2026-01-04 13:53:58 +01:00
Debug.WriteLine(ex);
}
}}