repo for my hex addons :3
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

demiplane excursions; add demiplane name; don't crash on unbound demiplanes

+149 -33
+1 -1
project/hexic/build.gradle.kts
··· 243 243 entrypoint("cardinal-components", "org.eu.net.pool.hexic.ComponentInit") 244 244 mixins("hexic.mixins.json") 245 245 mixins("hexic.client.mixins.json", Environment.Client) 246 - cardinalComponents("player_wisp", "server_info", "murmur", "reveal", "cat") 246 + cardinalComponents("player_wisp", "server_info", "excursion", "murmur", "reveal", "cat") 247 247 } 248 248 } 249 249
+3
project/hexic/changelog
··· 15 15 2.0.0 add view iotas (experimental!) 16 16 2.0.0 fix getting kicked if mediaweave sends an overly long message 17 17 2.0.0 load config from config/*.properties instead of config/jvm.properties 18 + 2.0.0 made demiplanes not instantly kill you and void your items if you look at them wrong 19 + 2.0.0 made demiplanes remember where they were entered from 18 20 2.0.0 made stringworms obtainable 19 21 2.0.0 mediaweave now applies to all chat messages 20 22 2.0.0 !mediaweave now uses trinket slots ··· 27 29 2.0.0 remove snow pattern & fix lithium incompatibility 28 30 2.0.0 !remove suffering 29 31 2.0.0 rename media bundles to casting pouches 32 + 2.0.0 sent Stickia to the milk dimension 30 33 2.0.0 textures now use JPEG XL
+4 -1
project/hexic/src/main/resources/data/hexcasting/tags/action/per_world_pattern.json
··· 1 1 { 2 2 "values": [ 3 - "hexic:moveentity" 3 + "hexic:moveentity", 4 + "hexic:makeworld", 5 + "hexic:attachworld", 6 + "hexic:deleteworld" 4 7 ] 5 8 }
+4 -1
project/hexic/src/main/resources/data/hexcasting/tags/action/requires_enlightenment.json
··· 1 1 { 2 2 "values": [ 3 - "hexic:moveentity" 3 + "hexic:moveentity", 4 + "hexic:makeworld", 5 + "hexic:attachworld", 6 + "hexic:deleteworld" 4 7 ] 5 8 }
+108 -26
project/hexic/src/main/scala/org/eu/net/pool/hexic/main.scala
··· 89 89 import java.lang.invoke.MethodHandles 90 90 import java.lang.reflect.{Constructor, Field, Member, Method} 91 91 import java.nio.ByteBuffer 92 - import java.nio.file.{Files, Path, Paths, StandardOpenOption} 92 + import java.nio.file.{FileSystemException, Files, Path, Paths, StandardOpenOption} 93 93 import java.util.{Optional, UUID} 94 94 import java.{lang, util} 95 95 import scala.annotation.unchecked.uncheckedVariance ··· 181 181 import java.math.BigInteger 182 182 import net.minecraft.entity.ItemEntity 183 183 import net.minecraft.entity.ExperienceOrbEntity 184 - import net.minecraft.entity.effect.StatusEffects 184 + import net.minecraft.entity.damage.DamageSources 185 + import net.minecraft.entity.effect.{StatusEffectInstance, StatusEffects} 185 186 import net.minecraft.network.packet.s2c.play.PositionFlag 186 187 import net.minecraft.predicate.entity.EntityPredicates 187 188 import net.minecraft.world.chunk.{ChunkStatus, WorldChunk} ··· 403 404 def sync()(using server: MinecraftServer): Unit = server.getOverworld.getLevelProperties.syncComponent(key) 404 405 private[hexic] def register(using fac: LevelComponentFactoryRegistry) = 405 406 fac.register(key, _ => ServerInfoComponent()) 407 + 408 + class ExcursionComponent(var enteredDemiplaneTick: Long = 0, var excursion: Option[(RegistryKey[World], Vec3d)] = None) extends Component: 409 + override def readFromNbt(tag: NbtCompound): Unit = 410 + enteredDemiplaneTick = tag.getLong("grace") 411 + excursion = for 412 + case c: NbtString <- Option(tag.get("dim")) 413 + id <- Option(Identifier.tryParse(c.asString)) 414 + yield (RegistryKey.of(RegistryKeys.WORLD, id), Vec3d(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z"))) 415 + override def writeToNbt(tag: NbtCompound): Unit = 416 + tag.putLong("grace", enteredDemiplaneTick) 417 + for world -> pos <- excursion do 418 + tag.putString("dim", world.getValue.toString) 419 + tag.putDouble("x", pos.getX) 420 + tag.putDouble("y", pos.getY) 421 + tag.putDouble("z", pos.getZ) 422 + object ExcursionComponent: 423 + given key: ComponentKey[ExcursionComponent] = ComponentRegistry.getOrCreate("excursion", classOf[ExcursionComponent]) 406 424 407 425 extension [S, T <: ArgumentBuilder[S, T]] (builder: T) 408 426 def literal(name: String)(body: LiteralArgumentBuilder[S] => Unit): T = builder.`then`(LiteralArgumentBuilder.literal[S](name).tap(body)) ··· 782 800 val img = vm.getImage 783 801 Some(CastingImage(img.getStack:+ListIota(img.getParenthesized.map(_.getIota)):+DoubleIota(img.getParenCount), 0, Seq(), false, img.getOpsConsumed, img.getUserData, null), ResolvedPatternType.EVALUATED) 784 802 case _ => None 785 - private [hexic] def getPocketName(pocket: String) = Text.of(pocketNames(getPocketID(Identifier.tryParse(pocket)).get)) 803 + private [hexic] def getPocketName(pocket: String) = Text.of("Demiplane " + pocketNames(getPocketID(Identifier.tryParse(pocket)).get)) 786 804 787 805 val _ = 788 806 Interop.playerDeathHook = (p: PlayerEntity, out: util.List[ItemStack]) => ··· 794 812 out.add(c.leftWeave) 795 813 c.leftWeave = ItemStack.EMPTY 796 814 815 + given demiplaneExtensions: AnyRef with 816 + extension (w: ServerWorld) 817 + def meta: String MutableFunction Option[String] = 818 + val path = w.getServer.getSavePath(WorldSavePath.ROOT).resolve(s"dimensions/${w.getRegistryKey.getValue.getNamespace}/${w.getRegistryKey.getValue.getPath}") 819 + new MutableFunction: 820 + def apply(name: String) = try Option(Files.getAttribute(path, "user:" + name)).asInstanceOf[Option[Array[Byte]]].map(buf => String(buf, StandardCharsets.UTF_8)) catch case _: FileSystemException => None 821 + def update(name: String, value: Option[String]): Unit = Files.setAttribute(path, "user:" + name, value.map(StandardCharsets.UTF_8.encode).orNull) 822 + def parentInfo: Option[(RegistryKey[World], BlockPos)] = for 823 + parentStr <- w.meta("parent") 824 + parentId <- Option(Identifier.tryParse(parentStr)) 825 + parentKey = RegistryKey.of(RegistryKeys.WORLD, parentId) 826 + if w.getServer.getWorld(parentKey) ne null 827 + x <- w.meta("bound_x").flatMap(v => Try(Integer.parseInt(v)).toOption) 828 + y <- w.meta("bound_y").flatMap(v => Try(Integer.parseInt(v)).toOption) 829 + z <- w.meta("bound_z").flatMap(v => Try(Integer.parseInt(v)).toOption) 830 + yield parentKey -> BlockPos(x, y, z) 831 + def parentInfo_=(parent: Option[(RegistryKey[World], BlockPos)]) = 832 + parent.fold { 833 + w.meta("parent") = None 834 + w.meta("bound_x") = None 835 + w.meta("bound_y") = None 836 + w.meta("bound_z") = None 837 + } { (world, pos) => 838 + w.meta("parent") = Some(world.getValue.toString) 839 + w.meta("bound_x") = Some(pos.getX.toString) 840 + w.meta("bound_y") = Some(pos.getY.toString) 841 + w.meta("bound_z") = Some(pos.getZ.toString) 842 + } 843 + 844 + object JavaPlaneAccess: 845 + def logExcursion(sp: ServerPlayerEntity) = 846 + val c = sp.component[ExcursionComponent] 847 + val curDim = sp.getWorld.getRegistryKey 848 + c.excursion = Some(curDim, sp.getPos) 849 + c.enteredDemiplaneTick = sp.getWorld.getTime 850 + sp.syncComponent(ExcursionComponent.key) 851 + def getDefaultExcursion(/** must be a demiplane */ world: ServerWorld): (ServerWorld, Vec3d) = 852 + locally: 853 + // if the plane is bound, where it's bound to is the default excursion 854 + for 855 + case (key, pos) <- world.parentInfo 856 + boundWorld <- Option(world.getServer.getWorld(key)) 857 + yield 858 + (boundWorld, Vec3d.ofBottomCenter(pos)) 859 + .getOrElse: 860 + // idfk just go to spawn or something 861 + // someone would never do something silly like setting the world spawn to y 10000 or something like that riiiight? 862 + (world.getServer.getOverworld, Vec3d.ofBottomCenter(world.getServer.getOverworld.getSpawnPos)) 863 + def findExcursion(/* must be in a demiplane */ sp: ServerPlayerEntity): (ServerWorld, Vec3d) = 864 + locally: 865 + // the happy path, assuming we have a player involved 866 + for 867 + case (key, pos) <- sp.component[ExcursionComponent].excursion 868 + world <- Option(sp.getServer.getWorld(key)) 869 + yield 870 + (world, pos) 871 + .getOrElse: 872 + given_Logger.warn(s"$sp claims to have never entered a demiplane") 873 + getDefaultExcursion(sp.getServerWorld) 874 + def findExcursion(target: Entity)(using env: CastingEnvironment): (ServerWorld, Vec3d) = 875 + env match 876 + case p: PlayerBasedCastEnv => 877 + val (world, pos) = findExcursion(p.getCaster) 878 + world -> pos.add(target.getPos).subtract(p.getCaster.getPos) 879 + case _ => target match 880 + case p: ServerPlayerEntity => findExcursion(p) 881 + case _ => target.getWorld match 882 + case sw: ServerWorld => getDefaultExcursion(sw) 883 + case _ => throw new IllegalStateException("why are you casting Spatial Interchange client-sided") 884 + def sendDirectlyToHell(sp: ServerPlayerEntity) = 885 + val c = sp.component[ExcursionComponent] 886 + if c.enteredDemiplaneTick + cfg[Int]("hexic.demiplaneGracePeriod").getOrElse(5) > sp.getWorld.getTime then 887 + given_Logger.warn(s"$sp found outside kitchen") 888 + sp.requestTeleport(5.5, 1.0, 5.5) 889 + else 890 + given_Logger.warn(s"$sp broke out of cell ${sp.getWorld.getRegistryKey}! mods, please check if the borders are broken") 891 + val (world, pos) = findExcursion(sp) 892 + val sp2 = FabricDimensions.teleport(sp, world, TeleportTarget(pos, Vec3d.ZERO, sp.getYaw, sp.getPitch)) 893 + sp2.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 30*20)) 894 + sp2.addStatusEffect(StatusEffectInstance(StatusEffects.BLINDNESS, 30*20)) 895 + sp2.damage(sp2.getWorld.getDamageSources.outOfWorld, sp2.getMaxHealth / 2) 896 + 797 897 given Codec[Int] = Codec.INT.xmap(p => p, p => p) 798 898 799 899 type Media = Long ··· 1115 1215 server.submit((() => handle.asWorld.getChunkManager.setChunkForced(ChunkPos.ORIGIN, true)): Runnable) 1116 1216 handle 1117 1217 ) 1118 - extension (w: ServerWorld) 1119 - def meta: String MutableFunction Option[String] = 1120 - val path = w.getServer.getSavePath(WorldSavePath.ROOT).resolve(s"dimensions/${w.getRegistryKey.getValue.getNamespace}/${w.getRegistryKey.getValue.getPath}") 1121 - new MutableFunction: 1122 - def apply(name: String) = Option(Files.getAttribute(path, "user:" + name)).asInstanceOf[Option[Array[Byte]]].map(buf => String(buf, StandardCharsets.UTF_8)) 1123 - def update(name: String, value: Option[String]): Unit = Files.setAttribute(path, "user:" + name, value.map(StandardCharsets.UTF_8.encode).orNull) 1124 - def parentInfo: Option[(RegistryKey[World], BlockPos)] = w.meta("parent").flatMap(value => Option(Identifier.tryParse(value))).map(RegistryKey.of(RegistryKeys.WORLD, _)).filter(w.getServer.getWorld(_) ne null).map((_, BlockPos(Integer.parseInt(w.meta("bound_x").get), Integer.parseInt(w.meta("bound_y").get), Integer.parseInt(w.meta("bound_z").get)))) 1125 - def parentInfo_=(parent: Option[(RegistryKey[World], BlockPos)]) = 1126 - parent.fold { 1127 - w.meta("parent") = None 1128 - w.meta("bound_x") = None 1129 - w.meta("bound_y") = None 1130 - w.meta("bound_z") = None 1131 - } { (world, pos) => 1132 - w.meta("parent") = Some(world.getValue.toString) 1133 - w.meta("bound_x") = Some(pos.getX.toString) 1134 - w.meta("bound_y") = Some(pos.getY.toString) 1135 - w.meta("bound_z") = Some(pos.getZ.toString) 1136 - } 1137 1218 extension (server: MinecraftServer) 1138 1219 def savedPlanes = 1139 1220 val file = server.getSavePath(WorldSavePath("fresh")) ··· 1360 1441 case p: PlayerEntity => 1361 1442 // thanks but please stop eating my ears 1362 1443 if isDev then println(s"Teleporting player $p") 1363 - FabricDimensions.teleport(p, outer, TeleportTarget(pos, Vec3d.ZERO, p.getYaw, p.getPitch)) 1364 - if !p.isCreative && !p.isSpectator then p.kill() 1444 + val p2 = FabricDimensions.teleport(p, outer, TeleportTarget(pos, Vec3d.ZERO, p.getYaw, p.getPitch)) 1445 + if !p2.isCreative && !p2.isSpectator then p2.kill() 1365 1446 case e: LivingEntity => 1366 1447 e.kill() 1367 1448 if isDev then println(s"Killing living entity $e") ··· 1372 1453 (e: LivingEntityAccess).callUpdatePostDeath() 1373 1454 case e => 1374 1455 if isDev then println(s"Killing nonliving entity $e") 1375 - FabricDimensions.teleport(e, outer, TeleportTarget(pos, Vec3d.ZERO, e.getYaw, e.getPitch)) 1376 - e.kill() 1456 + val e2 = FabricDimensions.teleport(e, outer, TeleportTarget(pos, Vec3d.ZERO, e.getYaw, e.getPitch)) 1457 + e2.kill() 1377 1458 pass += 1 1378 1459 if isDev then println(s"Proceeding to pass ${pass}") 1379 1460 recurse ··· 1723 1804 override def registerEntityComponentFactories(using r: EntityComponentFactoryRegistry): Unit = 1724 1805 PlayerInfoComponent.register 1725 1806 r.registerForPlayers(summon[ComponentKey[MurmurCache]], _ => MurmurCache(None), RespawnCopyStrategy.ALWAYS_COPY) 1807 + r.registerForPlayers(summon[ComponentKey[ExcursionComponent]], _ => ExcursionComponent(), RespawnCopyStrategy.ALWAYS_COPY) 1726 1808 r.registerForPlayers(summon[ComponentKey[RevealComponent]], _ => RevealComponent(Seq.empty), RespawnCopyStrategy.LOSSLESS_ONLY) 1727 1809 r.registerForPlayers(CatHolder.key, new CatHolder(_), RespawnCopyStrategy.NEVER_COPY) 1728 1810 override def registerLevelComponentFactories(using LevelComponentFactoryRegistry): Unit =
+1 -1
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/DimIotaMixin.java
··· 20 20 abstract class DimIotaMixin { 21 21 @WrapOperation(method = "display", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;of(Ljava/lang/String;)Lnet/minecraft/text/Text;")) 22 22 Text getName(String dim, Operation<Text> original) { 23 - return dim.startsWith("hexic:fresh_") ? org.eu.net.pool.hexic.Extern.getPocketName(dim) : original.call(dim); 23 + return dim.startsWith("hexic:fresh-") ? org.eu.net.pool.hexic.Extern.getPocketName(dim) : original.call(dim); 24 24 } 25 25 26 26 @WrapMethod(method = "deserialize(Lnet/minecraft/nbt/NbtElement;Lnet/minecraft/server/world/ServerWorld;)Lnet/beholderface/oneironaut/casting/iotatypes/DimIota;")
+11 -2
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/EntityMixin.java
··· 7 7 import net.minecraft.entity.damage.DamageSource; 8 8 import net.minecraft.entity.damage.DamageSources; 9 9 import net.minecraft.entity.player.PlayerEntity; 10 + import net.minecraft.server.network.ServerPlayerEntity; 10 11 import net.minecraft.util.CuboidBlockIterator; 11 12 import net.minecraft.util.math.BlockPos; 12 13 import net.minecraft.util.math.Box; 13 14 import net.minecraft.util.math.MathHelper; 14 15 import net.minecraft.util.math.Vec3d; 15 16 import net.minecraft.world.World; 17 + import org.eu.net.pool.hexic.JavaPlaneAccess; 16 18 import org.objectweb.asm.Opcodes; 17 19 import org.spongepowered.asm.mixin.Mixin; 18 20 import org.spongepowered.asm.mixin.Shadow; ··· 38 40 39 41 @Shadow public abstract boolean damage(DamageSource source, float amount); 40 42 43 + @Shadow public abstract boolean isPlayer(); 44 + 41 45 @Inject(at = @At("TAIL"), method = {"attemptTickInVoid", "method_31473"}, cancellable = true) 42 46 void attemptTickInVoidBlocks(CallbackInfo ci) { 43 47 Box box = getBoundingBox(); 44 48 if (!((Object) this instanceof PlayerEntity p && (p.isCreative() || p.isSpectator()))) { 45 49 var id = getWorld().getRegistryKey().getValue(); 46 50 if (id.getNamespace().equals("hexic") && id.getPath().startsWith("fresh-") && (box.minX < 0 || box.minY < 0 || box.minZ < 0 || box.maxX > 11 || box.maxY > 11 || box.maxZ > 11)) { 47 - damage(getWorld().getDamageSources().outsideBorder(), Float.MAX_VALUE); 48 - remove(Entity.RemovalReason.KILLED); 51 + if ((Object) this instanceof PlayerEntity p) { 52 + if (p instanceof ServerPlayerEntity sp) { 53 + JavaPlaneAccess.sendDirectlyToHell(sp); 54 + } 55 + } else { 56 + discard(); 57 + } 49 58 ci.cancel(); 50 59 return; 51 60 }
+17 -1
project/hexic/src/main/scala/org/eu/net/pool/hexic/mixin/OpDimTeleport$SpellMixin.java
··· 3 3 import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; 4 4 import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 5 5 import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 6 + import net.minecraft.entity.Entity; 7 + import net.minecraft.server.network.ServerPlayerEntity; 6 8 import net.minecraft.server.world.ServerWorld; 7 9 8 10 import net.minecraft.util.Identifier; 9 11 import net.minecraft.util.math.Vec3d; 10 12 import net.minecraft.world.dimension.DimensionType; 13 + import org.eu.net.pool.hexic.JavaPlaneAccess; 11 14 import org.spongepowered.asm.mixin.*; 12 15 import org.spongepowered.asm.mixin.injection.At; 13 16 import org.spongepowered.asm.mixin.injection.Inject; ··· 15 18 16 19 @Mixin(targets = "net.beholderface.oneironaut.casting.patterns.spells.great.OpDimTeleport$Spell") 17 20 class OpDimTeleport$SpellMixin { 21 + @Shadow private Entity target; 22 + @Shadow @Final @Mutable private ServerWorld origin; 18 23 @Shadow @Final private ServerWorld destination; 19 24 @Shadow @Final @Mutable private Vec3d coords; 25 + @Unique private boolean fromDemiplane; 20 26 @Unique private boolean toDemiplane; 21 27 @Inject(method = "cast(Lat/petrak/hexcasting/api/casting/eval/CastingEnvironment;)V", at = @At("HEAD")) 22 28 void preCast(CastingEnvironment env, CallbackInfo ci) { 29 + Identifier sourceID = origin.getRegistryKey().getValue(); 30 + fromDemiplane = sourceID.getNamespace().equals("hexic") && sourceID.getPath().startsWith("fresh-"); 23 31 Identifier targetID = destination.getRegistryKey().getValue(); 24 32 toDemiplane = targetID.getNamespace().equals("hexic") && targetID.getPath().startsWith("fresh-"); 25 - if (toDemiplane) { 33 + if (toDemiplane && !fromDemiplane) { 34 + // entering a demiplane for the first time, try to make sure nobody goes to the milk dimension 26 35 coords = new Vec3d(5.5, 1.0, 5.5); 36 + if (target instanceof ServerPlayerEntity sp) JavaPlaneAccess.logExcursion(sp); 37 + } else if (fromDemiplane && !toDemiplane) { 38 + // change ~origin~ and ~coords~ to the excursion; beholderface will handle the rest 39 + // this /definitely/ won't break the NG discount :clueless: 40 + final var dest = JavaPlaneAccess.findExcursion(target, env); 41 + origin = dest._1; 42 + coords = dest._2; 27 43 } 28 44 } 29 45 @WrapOperation(method = "cast(Lat/petrak/hexcasting/api/casting/eval/CastingEnvironment;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/dimension/DimensionType;coordinateScale()D", ordinal = -1))