Skip to content

Commit 871f938

Browse files
authored
Fix issues related to invalid/overlapping patterns (#938)
2 parents b0daf39 + b7e74d0 commit 871f938

File tree

7 files changed

+70
-22
lines changed

7 files changed

+70
-22
lines changed

Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexAngle.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,19 @@ enum class HexAngle {
55

66
fun rotatedBy(a: HexAngle) = values()[(this.ordinal + a.ordinal) % values().size]
77
operator fun times(a: HexAngle) = this.rotatedBy(a)
8+
9+
companion object {
10+
fun fromChar(c: Char): HexAngle? {
11+
return when (c) {
12+
'w' -> FORWARD
13+
'e' -> RIGHT
14+
'd' -> RIGHT_BACK
15+
// for completeness ...
16+
's' -> BACK
17+
'a' -> LEFT_BACK
18+
'q' -> LEFT
19+
else -> null
20+
}
21+
}
22+
}
823
}

Common/src/main/java/at/petrak/hexcasting/api/casting/math/HexPattern.kt

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
99
import net.minecraft.nbt.CompoundTag
1010
import net.minecraft.nbt.Tag
1111
import net.minecraft.world.phys.Vec2
12+
import kotlin.jvm.Throws
1213

1314
/**
1415
* Sequence of angles to define a pattern traced.
@@ -130,11 +131,12 @@ data class HexPattern(val startDir: HexDir, val angles: MutableList<HexAngle> =
130131
const val TAG_ANGLES = "angles"
131132

132133
@JvmField
133-
val CODEC: Codec<HexPattern> = RecordCodecBuilder.create({instance -> instance.group(
134-
Codec.STRING.fieldOf(TAG_START_DIR).forGetter(HexPattern::anglesSignature),
135-
HexDir.CODEC.fieldOf(TAG_ANGLES).forGetter(HexPattern::startDir)
136-
).apply(instance, HexPattern::fromAngles)
137-
})
134+
val CODEC: Codec<HexPattern> = RecordCodecBuilder.create { instance ->
135+
instance.group(
136+
Codec.STRING.fieldOf(TAG_START_DIR).forGetter(HexPattern::anglesSignature),
137+
HexDir.CODEC.fieldOf(TAG_ANGLES).forGetter(HexPattern::startDir)
138+
).apply(instance, HexPattern::fromAnglesUnchecked)
139+
}
138140

139141
@JvmStatic
140142
fun isPattern(tag: CompoundTag): Boolean {
@@ -149,30 +151,61 @@ data class HexPattern(val startDir: HexDir, val angles: MutableList<HexAngle> =
149151
return HexPattern(startDir, angles.toMutableList())
150152
}
151153

154+
/**
155+
* Construct a [HexPattern] from an angle signature and starting direction.
156+
*
157+
* Throws if the signature contains an invalid character, or if the resulting pattern would contain overlaps.
158+
*/
152159
@JvmStatic
160+
@Throws(IllegalArgumentException::class, IllegalStateException::class)
153161
fun fromAngles(signature: String, startDir: HexDir): HexPattern {
154162
val out = HexPattern(startDir)
163+
164+
var cursor = HexCoord.Origin
155165
var compass = startDir
166+
val linesSeen = mutableSetOf(
167+
cursor to compass,
168+
cursor + compass to compass.rotatedBy(HexAngle.BACK),
169+
)
156170

157171
for ((idx, c) in signature.withIndex()) {
158-
val angle = when (c) {
159-
'w' -> HexAngle.FORWARD
160-
'e' -> HexAngle.RIGHT
161-
'd' -> HexAngle.RIGHT_BACK
162-
// for completeness ...
163-
's' -> HexAngle.BACK
164-
'a' -> HexAngle.LEFT_BACK
165-
'q' -> HexAngle.LEFT
166-
else -> throw IllegalArgumentException("Cannot match $c at idx $idx to a direction")
167-
}
172+
val angle = HexAngle.fromChar(c)
173+
?: throw IllegalArgumentException("Cannot match $c at idx $idx to a direction")
174+
175+
cursor += compass
168176
compass *= angle
169-
val success = out.tryAppendDir(compass)
170-
if (!success) {
177+
178+
if (
179+
!linesSeen.add(cursor to compass)
180+
// Line from here to there also blocks there to here
181+
|| !linesSeen.add(cursor + compass to compass.rotatedBy(HexAngle.BACK))
182+
) {
171183
throw IllegalStateException("Adding the angle $c at index $idx made the pattern invalid by looping back on itself")
172184
}
185+
186+
out.angles.add(angle)
173187
}
188+
174189
return out
175190
}
176191

192+
/**
193+
* Construct a [HexPattern] from an angle signature and starting direction, without checking for overlaps.
194+
*
195+
* Throws if the signature contains an invalid character.
196+
*/
197+
@JvmStatic
198+
@Throws(IllegalArgumentException::class)
199+
fun fromAnglesUnchecked(signature: String, startDir: HexDir): HexPattern {
200+
val out = HexPattern(startDir)
201+
202+
for ((idx, c) in signature.withIndex()) {
203+
val angle = HexAngle.fromChar(c)
204+
?: throw IllegalArgumentException("Cannot match $c at idx $idx to a direction")
205+
out.angles.add(angle)
206+
}
207+
208+
return out
209+
}
177210
}
178211
}

Common/src/main/java/at/petrak/hexcasting/common/casting/PatternRegistryManifest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,6 @@ public static HexPattern getCanonicalStrokesPerWorld(ResourceKey<ActionRegistryE
128128

129129
var sig = pair.getFirst();
130130
var entry = pair.getSecond();
131-
return HexPattern.fromAngles(sig, entry.canonicalStartDir());
131+
return HexPattern.fromAnglesUnchecked(sig, entry.canonicalStartDir());
132132
}
133133
}

Common/src/main/java/at/petrak/hexcasting/common/command/ListPerWorldPatternsCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private static int giveAll(CommandSourceStack source, Collection<ServerPlayer> t
9696
var found = save.lookupReverse(key);
9797
var signature = found.getFirst();
9898
var startDir = found.getSecond().canonicalStartDir();
99-
var pat = HexPattern.fromAngles(signature, startDir);
99+
var pat = HexPattern.fromAnglesUnchecked(signature, startDir);
100100

101101
var tag = new CompoundTag();
102102
tag.putString(ItemScroll.TAG_OP_ID, key.location().toString());

Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static ItemStack doStatic(ItemStack stack, RandomSource rand) {
4444
var patsTag = new ListTag();
4545
for (var patString : hex.getSecond()){
4646
var pieces = patString.split(" ");
47-
var pat = HexPattern.fromAngles(pieces[1],HexDir.fromString(pieces[0]));
47+
var pat = HexPattern.fromAnglesUnchecked(pieces[1],HexDir.fromString(pieces[0]));
4848
patsTag.add(IotaType.serialize(new PatternIota(pat)));
4949
}
5050

Common/src/main/java/at/petrak/hexcasting/interop/inline/HexPatternMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public Tuple<InlineMatch, Integer> getMatchAndGroup(MatchResult regexMatch, Matc
5353
angleSigs = "";
5454
}
5555
try{
56-
pat = HexPattern.fromAngles(angleSigs.toLowerCase(), dir);
56+
pat = HexPattern.fromAnglesUnchecked(angleSigs.toLowerCase(), dir);
5757
InlinePatternData patData = new InlinePatternData(pat);
5858
Style patDataStyle = patData.getExtraStyle();
5959
if(sizeModString != null && sizeModString.equals("+"))

Common/src/main/java/at/petrak/hexcasting/interop/patchouli/ManualPatternComponent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public List<HexPattern> getPatterns(UnaryOperator<IVariable> lookup) {
3333
RawPattern raw = new Gson().fromJson(json, RawPattern.class);
3434

3535
var dir = HexDir.fromString(raw.startdir);
36-
var pat = HexPattern.fromAngles(raw.signature, dir);
36+
var pat = HexPattern.fromAnglesUnchecked(raw.signature, dir);
3737
out.add(pat);
3838
}
3939

0 commit comments

Comments
 (0)