Skip to content

Commit

Permalink
Update Designator Shapes to add a hard 10000 cells limit in MP (#463)
Browse files Browse the repository at this point in the history
Added a patch to Designator Shapes flood fill code, which will temporarily set the maximum amount of cells (when in a Multiplayer session) to 10000 (if the actual setting is above that value).

When trying to designate over (roughly) 10900 cells, MP will fail and cause an exception due to packet size being too big. On top of that, if the packet size is in a very close to the packet size limit it may cause a remote packet read error for the clients (effectively kicking them).

This is an issue that can also happen with vanilla designators (among other things), but that is something that should be fixed on MP side. The most we can do in MP Compat is to try and mitigate it in mods that may encounter this issue, which this is trying to achieve. So until this is fixed by MP, it may be a good idea to have this little workaround here.
  • Loading branch information
SokyranTheDragon authored Aug 22, 2024
1 parent ae17a23 commit 58f6477
Showing 1 changed file with 54 additions and 1 deletion.
55 changes: 54 additions & 1 deletion Source/Mods/DesignatorShapes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,20 @@ namespace Multiplayer.Compat
[MpCompatFor("Merthsoft.DesignatorShapes")]
public class DesignatorShapes
{
#region Fields

private static FastInvokeHandler settingsGetter = (_, _) => null;
private static AccessTools.FieldRef<ModSettings, int> floodFillCellLimitField = null;

#endregion

#region Main patch

public DesignatorShapes(ModContentPack mod)
{
// Init harmony patches
MpCompatPatchLoader.LoadPatch(this);

// Defs aren't setup yet, so we need to work on them later
LongEventHandler.ExecuteWhenFinished(LatePatch);

Expand All @@ -34,13 +46,54 @@ private static void LatePatch()
var pauseOnSelectionField = AccessTools.DeclaredField(shapeDefType, "pauseOnSelection");

var allDatabaseDefs = AccessTools.DeclaredPropertyGetter(typeof(DefDatabase<>).MakeGenericType(shapeDefType), "AllDefs");
var defsDatabase = allDatabaseDefs.Invoke(null, Array.Empty<object>());
var defsDatabase = allDatabaseDefs.Invoke(null, []);

if (defsDatabase is IEnumerable defs)
{
foreach (var def in defs)
pauseOnSelectionField.SetValue(def, false);
}

// Init quick access to DesignatorShapes.Settings. field
settingsGetter = MethodInvoker.GetHandler(AccessTools.DeclaredPropertyGetter(
"Merthsoft.DesignatorShapes.DesignatorShapes:Settings"));
floodFillCellLimitField = AccessTools.FieldRefAccess<int>(
"Merthsoft.DesignatorShapes.DesignatorSettings:FloodFillCellLimit");
}

#endregion

#region Flood fill cell limit

// If the flood fill limit is over 10000 cells, then it could cause an error
// when writing/reading the packet. Temporarily limit it to 10000 cells.

[MpCompatPrefix("Merthsoft.DesignatorShapes.Shapes.FloodFill", "Fill")]
private static void PreFloodFill(ref int? __state)
{
if (!MP.IsInMultiplayer)
return;

// Just a check that the field and getter were initialized, and that
// the mod didn't change too much (getter returns ModSettings object)
if (floodFillCellLimitField == null || settingsGetter(null) is not ModSettings settings)
return;

ref var limit = ref floodFillCellLimitField(settings);
if (limit <= 10000)
return;

__state = limit;
limit = 10000;
}

[MpCompatFinalizer("Merthsoft.DesignatorShapes.Shapes.FloodFill", "Fill")]
private static void PostFloodFill(int? __state)
{
if (__state != null)
floodFillCellLimitField(settingsGetter(null) as ModSettings) = __state.Value;
}

#endregion
}
}

0 comments on commit 58f6477

Please sign in to comment.