Client LuaCsForBarotrauma
IdRemap.cs
1 #nullable enable
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Xml.Linq;
7 
8 namespace Barotrauma
9 {
10  public sealed class IdRemap
11  {
12  public static readonly IdRemap DiscardId = new IdRemap(null, -1);
13 
14  private int maxId;
15 
16  private readonly List<Range<int>>? srcRanges;
17  private readonly int destOffset;
18 
19  public IdRemap(XElement? parentElement, int offset)
20  {
21  destOffset = offset;
22  if (parentElement is { HasElements: true })
23  {
24  srcRanges = new List<Range<int>>();
25  foreach (var subElement in parentElement.Elements())
26  {
27  int id = subElement.GetAttributeInt("ID", -1);
28  if (id > 0) { InsertId(id); }
29  }
30  maxId = GetOffsetId(srcRanges.Any() ? srcRanges.Last().End : offset);
31  }
32  else
33  {
34  maxId = offset;
35  }
36  }
37 
38  public void AssignMaxId(out ushort result)
39  {
40  maxId++;
41  result = (ushort)maxId;
42  }
43 
44  private void InsertId(int id)
45  {
46  if (srcRanges is null) { throw new NullReferenceException("Called InsertId when srcRanges is null"); }
47 
48  void tryMergeRangeWithNext(int indexA)
49  {
50  int indexB = indexA + 1;
51 
52  if (indexA < 0 /* Index A out of bounds */
53  || indexB >= srcRanges.Count /* Index B out of bounds */)
54  {
55  return;
56  }
57 
58  Range<int> rangeA = srcRanges[indexA];
59  Range<int> rangeB = srcRanges[indexB];
60 
61  if ((rangeA.End+1) >= rangeB.Start) //The end of range A is right before the start of range B, this should be one range
62  {
63  srcRanges[indexA] = new Range<int>(rangeA.Start, rangeB.End);
64  srcRanges.RemoveAt(indexB);
65  }
66  }
67 
68  int insertIndex = srcRanges.Count;
69  for (int i = 0; i < srcRanges.Count; i++)
70  {
71  if (srcRanges[i].Contains(id)) //We already have a range that contains this ID, duplicates are invalid input!
72  {
73  throw new InvalidOperationException($"Duplicate ID: {id}");
74  }
75  if (srcRanges[i].Start > id) //ID is between srcRanges[i-1] and srcRanges[i], insert at i
76  {
77  insertIndex = i;
78  break;
79  }
80  }
81  srcRanges.Insert(insertIndex, new Range<int>(id, id)); //Insert new range consisting of solely the new ID
82  tryMergeRangeWithNext(insertIndex); //Try merging new range with the one that comes after it
83  tryMergeRangeWithNext(insertIndex - 1); //Try merging new range with the one that comes before it
84  }
85 
86  public ushort GetOffsetId(XElement element)
87  {
88  return GetOffsetId(element.GetAttributeInt("ID", 0));
89  }
90 
91  public ushort GetOffsetId(int id)
92  {
93  if (id <= 0) //Input cannot be remapped because it's negative
94  {
95  return 0;
96  }
97  if (destOffset < 0) //Remapper has been defined to discard all input
98  {
99  return 0;
100  }
101  if (srcRanges is null) //Remapper defines no source ranges so it just adds an offset
102  {
103  return (ushort)(id + destOffset);
104  }
105 
106  int rangeSize(in Range<int> r)
107  => r.End - r.Start + 1;
108 
109  int currOffset = destOffset;
110  for (int i = 0; i < srcRanges.Count; i++)
111  {
112  if (srcRanges[i].Contains(id))
113  {
114  //The source range for this ID has been found!
115  //The return value is such that all IDs that
116  //are returned by this remapper are contiguous,
117  //even if they weren't originally
118  return (ushort)(id - srcRanges[i].Start + currOffset);
119  }
120  currOffset += rangeSize(srcRanges[i]);
121  }
122  return 0;
123  }
124 
125  public static ushort DetermineNewOffset()
126  {
127  int largestEntityId = 0;
128  foreach (Entity e in Entity.GetEntities())
129  {
130  if (e.ID > Entity.ReservedIDStart || e is Submarine) { continue; }
131  largestEntityId = Math.Max(largestEntityId, e.ID);
132  }
133  return (ushort)(largestEntityId+1);
134  }
135  }
136 }
const ushort ReservedIDStart
Definition: Entity.cs:19
static IReadOnlyCollection< Entity > GetEntities()
Definition: Entity.cs:24
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
Definition: Entity.cs:43
ushort GetOffsetId(XElement element)
Definition: IdRemap.cs:86
IdRemap(XElement? parentElement, int offset)
Definition: IdRemap.cs:19
void AssignMaxId(out ushort result)
Definition: IdRemap.cs:38
static ushort DetermineNewOffset()
Definition: IdRemap.cs:125
ushort GetOffsetId(int id)
Definition: IdRemap.cs:91
static readonly IdRemap DiscardId
Definition: IdRemap.cs:12