add generated world, territory registries and serverside verification for only legit territories and worlds defined by server
This commit is contained in:
@@ -94,6 +94,16 @@ public partial class LightlessHub
|
||||
throw new HubException("Unsupported chat channel.");
|
||||
}
|
||||
|
||||
if (channel.WorldId == 0 || !WorldRegistry.IsKnownWorld(channel.WorldId))
|
||||
{
|
||||
throw new HubException("Unsupported chat channel.");
|
||||
}
|
||||
|
||||
if (presence.TerritoryId == 0 || !definition.TerritoryIds.Contains(presence.TerritoryId))
|
||||
{
|
||||
throw new HubException("Zone chat is only available in supported territories.");
|
||||
}
|
||||
|
||||
string? hashedCid = null;
|
||||
var isLightfinder = false;
|
||||
if (IsValidHashedCid(UserCharaIdent))
|
||||
@@ -110,6 +120,7 @@ public partial class LightlessHub
|
||||
UserUID,
|
||||
definition,
|
||||
channel.WorldId,
|
||||
presence.TerritoryId,
|
||||
hashedCid,
|
||||
isLightfinder,
|
||||
isActive: true);
|
||||
|
||||
@@ -43,7 +43,8 @@ public readonly record struct ZoneChannelDefinition(
|
||||
string Key,
|
||||
string DisplayName,
|
||||
ChatChannelDescriptor Descriptor,
|
||||
IReadOnlyList<string> TerritoryNames);
|
||||
IReadOnlyList<string> TerritoryNames,
|
||||
IReadOnlySet<ushort> TerritoryIds);
|
||||
|
||||
public readonly record struct ChannelKey(ChatChannelType Type, ushort WorldId, string CustomKey)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,10 @@ internal static class ChatZoneDefinitions
|
||||
{
|
||||
"Limsa Lominsa Lower Decks",
|
||||
"Limsa Lominsa Upper Decks"
|
||||
}),
|
||||
},
|
||||
TerritoryIds: TerritoryRegistry.GetIds(
|
||||
"Limsa Lominsa Lower Decks",
|
||||
"Limsa Lominsa Upper Decks")),
|
||||
new ZoneChannelDefinition(
|
||||
Key: "gridania",
|
||||
DisplayName: "Gridania",
|
||||
@@ -37,7 +40,10 @@ internal static class ChatZoneDefinitions
|
||||
{
|
||||
"New Gridania",
|
||||
"Old Gridania"
|
||||
}),
|
||||
},
|
||||
TerritoryIds: TerritoryRegistry.GetIds(
|
||||
"New Gridania",
|
||||
"Old Gridania")),
|
||||
new ZoneChannelDefinition(
|
||||
Key: "uldah",
|
||||
DisplayName: "Ul'dah",
|
||||
@@ -52,6 +58,10 @@ internal static class ChatZoneDefinitions
|
||||
{
|
||||
"Ul'dah - Steps of Nald",
|
||||
"Ul'dah - Steps of Thal"
|
||||
})
|
||||
},
|
||||
TerritoryIds: TerritoryRegistry.GetIds(
|
||||
"Ul'dah - Steps of Nald",
|
||||
"Ul'dah - Steps of Thal"))
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace LightlessSyncServer.Models;
|
||||
|
||||
internal readonly record struct TerritoryDefinition(
|
||||
ushort TerritoryId,
|
||||
string Name);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
namespace LightlessSyncServer.Models;
|
||||
|
||||
internal readonly record struct WorldDefinition(
|
||||
ushort WorldId,
|
||||
string Name,
|
||||
string Region,
|
||||
string DataCenter);
|
||||
@@ -0,0 +1,117 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace LightlessSyncServer.Models;
|
||||
|
||||
internal static class WorldRegistry
|
||||
{
|
||||
private static readonly WorldDefinition[] WorldArray = new[]
|
||||
{
|
||||
new WorldDefinition(80, "Cerberus", "Europe", "Chaos"),
|
||||
new WorldDefinition(83, "Louisoix", "Europe", "Chaos"),
|
||||
new WorldDefinition(71, "Moogle", "Europe", "Chaos"),
|
||||
new WorldDefinition(39, "Omega", "Europe", "Chaos"),
|
||||
new WorldDefinition(401, "Phantom", "Europe", "Chaos"),
|
||||
new WorldDefinition(97, "Ragnarok", "Europe", "Chaos"),
|
||||
new WorldDefinition(400, "Sagittarius", "Europe", "Chaos"),
|
||||
new WorldDefinition(85, "Spriggan", "Europe", "Chaos"),
|
||||
new WorldDefinition(402, "Alpha", "Europe", "Light"),
|
||||
new WorldDefinition(36, "Lich", "Europe", "Light"),
|
||||
new WorldDefinition(66, "Odin", "Europe", "Light"),
|
||||
new WorldDefinition(56, "Phoenix", "Europe", "Light"),
|
||||
new WorldDefinition(403, "Raiden", "Europe", "Light"),
|
||||
new WorldDefinition(67, "Shiva", "Europe", "Light"),
|
||||
new WorldDefinition(33, "Twintania", "Europe", "Light"),
|
||||
new WorldDefinition(42, "Zodiark", "Europe", "Light"),
|
||||
new WorldDefinition(90, "Aegis", "Japan", "Elemental"),
|
||||
new WorldDefinition(68, "Atomos", "Japan", "Elemental"),
|
||||
new WorldDefinition(45, "Carbuncle", "Japan", "Elemental"),
|
||||
new WorldDefinition(58, "Garuda", "Japan", "Elemental"),
|
||||
new WorldDefinition(94, "Gungnir", "Japan", "Elemental"),
|
||||
new WorldDefinition(49, "Kujata", "Japan", "Elemental"),
|
||||
new WorldDefinition(72, "Tonberry", "Japan", "Elemental"),
|
||||
new WorldDefinition(50, "Typhon", "Japan", "Elemental"),
|
||||
new WorldDefinition(43, "Alexander", "Japan", "Gaia"),
|
||||
new WorldDefinition(69, "Bahamut", "Japan", "Gaia"),
|
||||
new WorldDefinition(92, "Durandal", "Japan", "Gaia"),
|
||||
new WorldDefinition(46, "Fenrir", "Japan", "Gaia"),
|
||||
new WorldDefinition(59, "Ifrit", "Japan", "Gaia"),
|
||||
new WorldDefinition(98, "Ridill", "Japan", "Gaia"),
|
||||
new WorldDefinition(76, "Tiamat", "Japan", "Gaia"),
|
||||
new WorldDefinition(51, "Ultima", "Japan", "Gaia"),
|
||||
new WorldDefinition(44, "Anima", "Japan", "Mana"),
|
||||
new WorldDefinition(23, "Asura", "Japan", "Mana"),
|
||||
new WorldDefinition(70, "Chocobo", "Japan", "Mana"),
|
||||
new WorldDefinition(47, "Hades", "Japan", "Mana"),
|
||||
new WorldDefinition(48, "Ixion", "Japan", "Mana"),
|
||||
new WorldDefinition(96, "Masamune", "Japan", "Mana"),
|
||||
new WorldDefinition(28, "Pandaemonium", "Japan", "Mana"),
|
||||
new WorldDefinition(61, "Titan", "Japan", "Mana"),
|
||||
new WorldDefinition(24, "Belias", "Japan", "Meteor"),
|
||||
new WorldDefinition(82, "Mandragora", "Japan", "Meteor"),
|
||||
new WorldDefinition(60, "Ramuh", "Japan", "Meteor"),
|
||||
new WorldDefinition(29, "Shinryu", "Japan", "Meteor"),
|
||||
new WorldDefinition(30, "Unicorn", "Japan", "Meteor"),
|
||||
new WorldDefinition(52, "Valefor", "Japan", "Meteor"),
|
||||
new WorldDefinition(31, "Yojimbo", "Japan", "Meteor"),
|
||||
new WorldDefinition(32, "Zeromus", "Japan", "Meteor"),
|
||||
new WorldDefinition(73, "Adamantoise", "North America", "Aether"),
|
||||
new WorldDefinition(79, "Cactuar", "North America", "Aether"),
|
||||
new WorldDefinition(54, "Faerie", "North America", "Aether"),
|
||||
new WorldDefinition(63, "Gilgamesh", "North America", "Aether"),
|
||||
new WorldDefinition(40, "Jenova", "North America", "Aether"),
|
||||
new WorldDefinition(65, "Midgardsormr", "North America", "Aether"),
|
||||
new WorldDefinition(99, "Sargatanas", "North America", "Aether"),
|
||||
new WorldDefinition(57, "Siren", "North America", "Aether"),
|
||||
new WorldDefinition(91, "Balmung", "North America", "Crystal"),
|
||||
new WorldDefinition(34, "Brynhildr", "North America", "Crystal"),
|
||||
new WorldDefinition(74, "Coeurl", "North America", "Crystal"),
|
||||
new WorldDefinition(62, "Diabolos", "North America", "Crystal"),
|
||||
new WorldDefinition(81, "Goblin", "North America", "Crystal"),
|
||||
new WorldDefinition(75, "Malboro", "North America", "Crystal"),
|
||||
new WorldDefinition(37, "Mateus", "North America", "Crystal"),
|
||||
new WorldDefinition(41, "Zalera", "North America", "Crystal"),
|
||||
new WorldDefinition(408, "Cuchulainn", "North America", "Dynamis"),
|
||||
new WorldDefinition(411, "Golem", "North America", "Dynamis"),
|
||||
new WorldDefinition(406, "Halicarnassus", "North America", "Dynamis"),
|
||||
new WorldDefinition(409, "Kraken", "North America", "Dynamis"),
|
||||
new WorldDefinition(407, "Maduin", "North America", "Dynamis"),
|
||||
new WorldDefinition(404, "Marilith", "North America", "Dynamis"),
|
||||
new WorldDefinition(410, "Rafflesia", "North America", "Dynamis"),
|
||||
new WorldDefinition(405, "Seraph", "North America", "Dynamis"),
|
||||
new WorldDefinition(78, "Behemoth", "North America", "Primal"),
|
||||
new WorldDefinition(93, "Excalibur", "North America", "Primal"),
|
||||
new WorldDefinition(53, "Exodus", "North America", "Primal"),
|
||||
new WorldDefinition(35, "Famfrit", "North America", "Primal"),
|
||||
new WorldDefinition(95, "Hyperion", "North America", "Primal"),
|
||||
new WorldDefinition(55, "Lamia", "North America", "Primal"),
|
||||
new WorldDefinition(64, "Leviathan", "North America", "Primal"),
|
||||
new WorldDefinition(77, "Ultros", "North America", "Primal"),
|
||||
new WorldDefinition(22, "Bismarck", "Oceania", "Materia"),
|
||||
new WorldDefinition(21, "Ravana", "Oceania", "Materia"),
|
||||
new WorldDefinition(86, "Sephirot", "Oceania", "Materia"),
|
||||
new WorldDefinition(87, "Sophia", "Oceania", "Materia"),
|
||||
new WorldDefinition(88, "Zurvan", "Oceania", "Materia"),
|
||||
};
|
||||
|
||||
public static IReadOnlyList<WorldDefinition> All { get; } = Array.AsReadOnly(WorldArray);
|
||||
public static IReadOnlyDictionary<ushort, WorldDefinition> ById { get; } = new ReadOnlyDictionary<ushort, WorldDefinition>(WorldArray.ToDictionary(w => w.WorldId));
|
||||
public static IReadOnlyDictionary<string, IReadOnlyList<WorldDefinition>> ByDataCenter { get; } = new ReadOnlyDictionary<string, IReadOnlyList<WorldDefinition>>(WorldArray
|
||||
.GroupBy(w => w.DataCenter, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(
|
||||
g => g.Key,
|
||||
g => (IReadOnlyList<WorldDefinition>)g.OrderBy(w => w.Name, StringComparer.Ordinal).ToArray(),
|
||||
StringComparer.OrdinalIgnoreCase));
|
||||
public static IReadOnlyDictionary<string, IReadOnlyList<WorldDefinition>> ByRegion { get; } = new ReadOnlyDictionary<string, IReadOnlyList<WorldDefinition>>(WorldArray
|
||||
.GroupBy(w => w.Region, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(
|
||||
g => g.Key,
|
||||
g => (IReadOnlyList<WorldDefinition>)g.OrderBy(w => w.Name, StringComparer.Ordinal).ToArray(),
|
||||
StringComparer.OrdinalIgnoreCase));
|
||||
|
||||
public static bool TryGet(ushort worldId, out WorldDefinition definition) => ById.TryGetValue(worldId, out definition);
|
||||
public static bool IsKnownWorld(ushort worldId) => ById.ContainsKey(worldId);
|
||||
}
|
||||
@@ -49,11 +49,24 @@ public sealed class ChatChannelService
|
||||
string userUid,
|
||||
ZoneChannelDefinition definition,
|
||||
ushort worldId,
|
||||
ushort territoryId,
|
||||
string? hashedCid,
|
||||
bool isLightfinder,
|
||||
bool isActive)
|
||||
{
|
||||
var descriptor = definition.Descriptor with { WorldId = worldId };
|
||||
if (worldId == 0 || !WorldRegistry.IsKnownWorld(worldId))
|
||||
{
|
||||
_logger.LogWarning("Rejected zone chat presence for {User} in {Zone}: unknown world {WorldId}", userUid, definition.Key, worldId);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!definition.TerritoryIds.Contains(territoryId))
|
||||
{
|
||||
_logger.LogWarning("Rejected zone chat presence for {User} in {Zone}: invalid territory {TerritoryId}", userUid, definition.Key, territoryId);
|
||||
return null;
|
||||
}
|
||||
|
||||
var descriptor = definition.Descriptor with { WorldId = worldId, ZoneId = territoryId };
|
||||
var participant = new ChatParticipantInfo(
|
||||
Token: string.Empty,
|
||||
UserUid: userUid,
|
||||
|
||||
Reference in New Issue
Block a user