Skip to content

Commit

Permalink
MSDP Cleanup towards getting an example handler.
Browse files Browse the repository at this point in the history
  • Loading branch information
HarryCordewener committed Jan 29, 2024
1 parent 8283815 commit 3a657a7
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 87 deletions.
2 changes: 1 addition & 1 deletion TelnetNegotiationCore.Functional/MSDPLibrary.fs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ module MSDPLibrary =
| JsonValueKind.Number -> encoding.GetBytes(jsonNode.AsValue().ToString()) |> List.ofArray
| JsonValueKind.True -> encoding.GetBytes("1") |> List.ofArray
| JsonValueKind.False -> encoding.GetBytes("0") |> List.ofArray
| JsonValueKind.Null -> encoding.GetBytes("null") |> List.ofArray
| JsonValueKind.Null -> encoding.GetBytes("-1") |> List.ofArray
| _ -> failwith "Invalid JSON value"
parseJsonValue jsonRootNode

Expand Down
201 changes: 115 additions & 86 deletions TelnetNegotiationCore/Handlers/MSDPServerHandler.cs
Original file line number Diff line number Diff line change
@@ -1,79 +1,91 @@
using System;
using Microsoft.FSharp.Data.UnitSystems.SI.UnitNames;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection.Emit;
using System.Runtime.Intrinsics.X86;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml;
using TelnetNegotiationCore.Functional;
using TelnetNegotiationCore.Interpreters;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace TelnetNegotiationCore.Handlers
{
/*
* https://tintin.mudhalla.net/protocols/msdp/
Reportable MSDP Variables
These variables are mere suggestions for MUDs wanting to implement MSDP. By using these reportable variables it'll be easier to create MSDP scripts that need little or no modification to work across MUDs. If you create your own set of variables in addition to these, it's suggested to create an extended online specification that describes your variables and their behavior. The SPECIFICATION variable can be used to link people to the web page.
General
"ACCOUNT_NAME" Name of the player account.
"CHARACTER_NAME" Name of the player character.
"SERVER_ID" Name of the MUD, or an otherwise unique ID.
"SERVER_TIME" The time on the server using either military or civilian time.
"SPECIFICATION" URL to the MUD's online MSDP specification, if any.
Character
"AFFECTS" Current affects in array format.
"ALIGNMENT" Current alignment.
"EXPERIENCE" Current total experience points. Use 0-100 for percentages.
"EXPERIENCE_MAX" Current maximum experience points. Use 100 for percentages.
"EXPERIENCE_TNL" Current total experience points Till Next Level. Use 0-100 for percentages.
"EXPERIENCE_TNL_MAX" Current maximum experience points Till Next Level. Use 100 for percentages.
"HEALTH" Current health points.
"HEALTH_MAX" Current maximum health points.
"LEVEL" Current level.
"MANA" Current mana points.
"MANA_MAX" Current maximum mana points.
"MONEY" Current amount of money.
"MOVEMENT" Current movement points.
"MOVEMENT_MAX" Current maximum movement points.
Combat
"OPPONENT_LEVEL" Level of opponent.
"OPPONENT_HEALTH" Current health points of opponent. Use 0-100 for percentages.
"OPPONENT_HEALTH_MAX" Current maximum health points of opponent. Use 100 for percentages.
"OPPONENT_NAME" Name of opponent.
"OPPONENT_STRENGTH" Relative strength of opponent, like the consider mud command.
Mapping
Indentation indicates the variable is nested within the parent variable using a table.
"ROOM"
"VNUM" A number uniquely identifying the room.
"NAME" The name of the room.
"AREA" The area the room is in.
"COORDS"
"X" The X coordinate of the room.
"Y" The Y coordinate of the room.
"Z" The Z coordinate of the room.
"TERRAIN" The terrain type of the room. Forest, Ocean, etc.
"EXITS" Nested abbreviated exit directions (n, e, w, etc) and corresponding destination VNUMs.
World
"WORLD_TIME" The in game time on the MUD using either military or civilian time.
Configurable MSDP Variables
Configurable variables are variables on the server that can be altered by the client. Implementing configurable variable support is optional.
General
"CLIENT_NAME" Name of the MUD client.
"CLIENT_VERSION" Version of the MUD client.
"PLUGIN_ID" Unique ID of the MSDP plugin/script.
*/

/// <summary>
/// A simple handler for MSDP that creates a workflow for responding with MSDP information.
/// </summary>
/// <remarks>
/// We need a way to Observe changes to the functions to properly support the REPORT action.
/// This is not currently implemented.
/// </remarks>
/// <param name="resolvers">
/// A dictionary that resolves Variables, commands, and lists. This has a very specific setup, and comes with a contract.
///
/// TODO: We can do this better somehow.
///
/// https://tintin.mudhalla.net/protocols/msdp/
/// Reportable MSDP Variables
/// These variables are mere suggestions for MUDs wanting to implement MSDP.By using these reportable variables it'll be easier to create MSDP scripts that need little or no modification to work across MUDs. If you create your own set of variables in addition to these, it's suggested to create an extended online specification that describes your variables and their behavior.The SPECIFICATION variable can be used to link people to the web page.
///
/// General
/// "ACCOUNT_NAME" Name of the player account.
/// "CHARACTER_NAME" Name of the player character.
/// "SERVER_ID" Name of the MUD, or an otherwise unique ID.
/// "SERVER_TIME" The time on the server using either military or civilian time.
/// "SPECIFICATION" URL to the MUD's online MSDP specification, if any.
///
/// Character
/// "AFFECTS" Current affects in array format.
/// "ALIGNMENT" Current alignment.
/// "EXPERIENCE" Current total experience points. Use 0-100 for percentages.
/// "EXPERIENCE_MAX" Current maximum experience points. Use 100 for percentages.
/// "EXPERIENCE_TNL" Current total experience points Till Next Level.Use 0-100 for percentages.
/// "EXPERIENCE_TNL_MAX" Current maximum experience points Till Next Level.Use 100 for percentages.
/// "HEALTH" Current health points.
/// "HEALTH_MAX" Current maximum health points.
/// "LEVEL" Current level.
/// "MANA" Current mana points.
/// "MANA_MAX" Current maximum mana points.
/// "MONEY" Current amount of money.
/// "MOVEMENT" Current movement points.
/// "MOVEMENT_MAX" Current maximum movement points.
///
/// Combat
/// "OPPONENT_LEVEL" Level of opponent.
/// "OPPONENT_HEALTH" Current health points of opponent.Use 0-100 for percentages.
/// "OPPONENT_HEALTH_MAX" Current maximum health points of opponent. Use 100 for percentages.
/// "OPPONENT_NAME" Name of opponent.
/// "OPPONENT_STRENGTH" Relative strength of opponent, like the consider mud command.
/// Mapping
/// Indentation indicates the variable is nested within the parent variable using a table.
/// "ROOM"
/// "VNUM" A number uniquely identifying the room.
/// "NAME" The name of the room.
/// "AREA" The area the room is in.
/// "COORDS"
/// "X" The X coordinate of the room.
/// "Y" The Y coordinate of the room.
/// "Z" The Z coordinate of the room.
/// "TERRAIN" The terrain type of the room. Forest, Ocean, etc.
/// "EXITS" Nested abbreviated exit directions (n, e, w, etc) and corresponding destination VNUMs.
///
/// World
/// "WORLD_TIME" The in game time on the MUD using either military or civilian time.
///
/// Configurable MSDP Variables
/// Configurable variables are variables on the server that can be altered by the client. Implementing configurable variable support is optional.
/// General
/// "CLIENT_NAME" Name of the MUD client.
/// "CLIENT_VERSION" Version of the MUD client.
/// "PLUGIN_ID" Unique ID of the MSDP plugin/script.
/// </param>
public class MSDPServerHandler(Dictionary<string, Func<string>> resolvers)
{
private Dictionary<string, Func<string>> Resolvers { get; } = resolvers;

public async Task HandleAsync(TelnetInterpreter telnet, string clientJson)
{
var json = JsonSerializer.Deserialize<dynamic>(clientJson);
Expand Down Expand Up @@ -117,32 +129,32 @@ public async Task HandleAsync(TelnetInterpreter telnet, string clientJson)
private async Task HandleListRequestAsync(TelnetInterpreter telnet, string item)
{
// TODO: Do something to check if ITEM is an Array or a single String here.
var found = resolvers.TryGetValue($"LIST.{item}", out var list);
var found = Resolvers.TryGetValue($"LIST.{item}", out var list);
var jsonString = $"{{{item}:{(found ? list : "null")}}}";
await telnet.CallbackNegotiationAsync(MSDPLibrary.Report(jsonString, telnet.CurrentEncoding));
}

private async Task HandleReportRequestAsync(TelnetInterpreter telnet, string item)
{
// TODO: Do something to check if ITEM is an Array or a single String here.
// TODO: Implement a Reporting system.
await HandleSendRequestAsync(telnet, item);
}
private async Task HandleReportRequestAsync(TelnetInterpreter telnet, string item) =>
await ExecuteOnAsync(item, async (val) =>
{
await HandleSendRequestAsync(telnet, val);
// TODO: Implement a Reporting system.
});

/// <summary>
/// The RESET command works like the LIST command, and can be used to reset groups of variables to their initial state.
/// Most commonly RESET will be called with REPORTABLE_VARIABLES or REPORTED_VARIABLES as the argument,
/// though any LIST option can be used.
/// </summary>
/// <param name="item">Item to reset</param>
private async Task HandleResetRequestAsync(string item)
{
// TODO: Do something to check if ITEM is an Array or a single String here.
// TODO: Reset it?
// TODO: Enable resetting other items we can LIST?
var found = resolvers.TryGetValue($"REPORTABLE_VARIABLES.{item}", out var list);
await Task.CompletedTask;
}
private async Task HandleResetRequestAsync(string item) =>
await ExecuteOnAsync(item, async (val) =>
{
// TODO: Reset it?
// TODO: Enable resetting other items we can LIST?
var found = Resolvers.TryGetValue($"REPORTABLE_VARIABLES.{val}", out var list);
await Task.CompletedTask;
});

/// <summary>
/// The SEND command can be used by either side, but should typically be used by the client.
Expand All @@ -152,24 +164,41 @@ private async Task HandleResetRequestAsync(string item)
/// </summary>
/// <param name="telnet">Telnet interpreter to send back negotiation with</param>
/// <param name="item">The item to send</param>
private async Task HandleSendRequestAsync(TelnetInterpreter telnet, string item)
{
// TODO: Do something to check if ITEM is an Array or a single String here.
var found = resolvers.TryGetValue($"REPORTABLE_VARIABLES.{item}", out var list);
var jsonString = $"{{{item}:{(found ? list : "null")}}}";
await telnet.CallbackNegotiationAsync(MSDPLibrary.Report(jsonString, telnet.CurrentEncoding));
}
private async Task HandleSendRequestAsync(TelnetInterpreter telnet, string item) =>
await ExecuteOnAsync(item, async (val) =>
{
var found = Resolvers.TryGetValue($"REPORTABLE_VARIABLES.{val}", out var list);
var jsonString = $"{{{val}:{(found ? list : "null")}}}";
await telnet.CallbackNegotiationAsync(MSDPLibrary.Report(jsonString, telnet.CurrentEncoding));
});

/// <summary>
/// The UNREPORT command is used to remove the report status of variables after the use of the REPORT command.
/// </summary>
/// <param name="item">The item to stop reporting on</param>
private async Task HandleUnReportRequestAsync(string item)
private async Task HandleUnReportRequestAsync(string item) =>
await ExecuteOnAsync(item, async (val) =>
{
// TODO: Remove them from the list of variables being reported.
var found = Resolvers.TryGetValue($"REPORTED_VARIABLES.{item}", out var list);
await Task.CompletedTask;
});

private async Task ExecuteOnAsync(string item, Func<string, Task> function)
{
var found = resolvers.TryGetValue($"REPORTED_VARIABLES.{item}", out var list);
// TODO: Remove them from the list of variables being reported.
// TODO: Do something to check if ITEM is an Array or a single String here.
await Task.CompletedTask;
string[] items;
if (item.StartsWith('['))
{
items = JsonSerializer.Deserialize<string[]>(item);
}
else
{
items = [item];
}
foreach(var val in items)
{
await function(val);
}
}
}
}

0 comments on commit 3a657a7

Please sign in to comment.