diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index ad406637..4e0ea027 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -689,15 +689,22 @@ number('3bar') => null number('2')+number('2') => 4 -### `str(expr, params? ... ), str(expr, param_list)` +### `str(expr)`,`str(expr, params? ... )`, `str(expr, param_list)` -Returns a formatted string representing expression. Accepts formatting style accepted by `String.format`. -Supported types (with `"%?"` syntax): +If called with one argument, returns string representation of such value. + +Otherwise, returns a formatted string representing the expression. Arguments for formatting can either be provided as + each consecutive parameter, or as a list which then would be the only extra parameter. To format one list argument + , you can use `str(list)`, or `str('foo %s', l(list))`. + +Accepts formatting style accepted by `String.format`. +Supported types (with `"%>"` syntax): * `d`, `o`, `x`: integers, octal, hex * `a`, `e`, `f`, `g`: floats * `b`: booleans * `s`: strings +* `%%`: '%' character
str(null) => null
@@ -2069,13 +2076,17 @@ Returns spawn potential at a location (1.16+ only)
Checks wordgen eligibility for a structure in a given chunk. If no structure is given, or `null`, then it will check
for all structures. If bounding box of the structures is also requested, it will compute size of potential
structures. This function, unlike other in the `structure*` category is not using world data nor accesses chunks
- making it preferred for scoping ungenerated terrain.
+ making it preferred for scoping ungenerated terrain, but it takes some compute money to calculate the structure.
+
+ Unlike `'structure'` this will return a tentative structure location. Random factors in world generation may prevent
+ actual structure from forming.
If structure is specified, it will return `null` if a chunk is not eligible, `true` if the structure should appear, or
-a pair of coordinates indicating bounding box of the structure.
+a map with two values: `'box'` for a pair of coordinates indicating bounding box of the structure, and `'pieces'` for
+list of elements of the structure (as a tuple), with its name, direction, and box coordinates of the piece.
If structure is not specified, it will return a set of structure names that are eligible, or a map with structures
-as keys, and pair of bounding box coordinates as values. An empty set or an empty map would indicate that nothing
+as keys, and same type of map values as with a single structure call. An empty set or an empty map would indicate that nothing
should be generated there.
@@ -2085,7 +2096,8 @@ Returns structure information for a given block position. Note that structure in
blocks from the same chunk. `structures` function can be called with a block, or a block and a structure name. In
the first case it returns a map of structures at a given position, keyed by structure name, with values indicating
the bounding box of the structure - a pair of two 3-value coords (see examples). When called with an extra structure
-name, returns list of components for that structure, with their name, direction and two sets of coordinates
+name, returns a map with two values, `'box'` for bounding box of the structure, and `'pieces'` for a list of
+components for that structure, with their name, direction and two sets of coordinates
indicating the bounding box of the structure piece.
### `structure_references(pos), structure_references(pos, structure_name)`
diff --git a/docs/scarpet/api/BlocksAndWorldAccess.md b/docs/scarpet/api/BlocksAndWorldAccess.md
index 17145b86..e6ae9e39 100644
--- a/docs/scarpet/api/BlocksAndWorldAccess.md
+++ b/docs/scarpet/api/BlocksAndWorldAccess.md
@@ -532,13 +532,17 @@ Returns spawn potential at a location (1.16+ only)
Checks wordgen eligibility for a structure in a given chunk. If no structure is given, or `null`, then it will check
for all structures. If bounding box of the structures is also requested, it will compute size of potential
structures. This function, unlike other in the `structure*` category is not using world data nor accesses chunks
- making it preferred for scoping ungenerated terrain.
+ making it preferred for scoping ungenerated terrain, but it takes some compute money to calculate the structure.
+
+ Unlike `'structure'` this will return a tentative structure location. Random factors in world generation may prevent
+ actual structure from forming.
If structure is specified, it will return `null` if a chunk is not eligible, `true` if the structure should appear, or
-a pair of coordinates indicating bounding box of the structure.
+a map with two values: `'box'` for a pair of coordinates indicating bounding box of the structure, and `'pieces'` for
+list of elements of the structure (as a tuple), with its name, direction, and box coordinates of the piece.
If structure is not specified, it will return a set of structure names that are eligible, or a map with structures
-as keys, and pair of bounding box coordinates as values. An empty set or an empty map would indicate that nothing
+as keys, and same type of map values as with a single structure call. An empty set or an empty map would indicate that nothing
should be generated there.
@@ -548,7 +552,8 @@ Returns structure information for a given block position. Note that structure in
blocks from the same chunk. `structures` function can be called with a block, or a block and a structure name. In
the first case it returns a map of structures at a given position, keyed by structure name, with values indicating
the bounding box of the structure - a pair of two 3-value coords (see examples). When called with an extra structure
-name, returns list of components for that structure, with their name, direction and two sets of coordinates
+name, returns a map with two values, `'box'` for bounding box of the structure, and `'pieces'` for a list of
+components for that structure, with their name, direction and two sets of coordinates
indicating the bounding box of the structure piece.
### `structure_references(pos), structure_references(pos, structure_name)`
diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java
index 44a673f9..41a00df4 100644
--- a/src/main/java/carpet/CarpetSettings.java
+++ b/src/main/java/carpet/CarpetSettings.java
@@ -583,4 +583,10 @@ public class CarpetSettings
category = CLIENT
)
public static boolean fogOff = false;
+
+ @Rule(
+ desc = "Creative No Clip",
+ category = {CREATIVE, CLIENT}
+ )
+ public static boolean creativeNoClip = false;
}
diff --git a/src/main/java/carpet/helpers/FeatureGenerator.java b/src/main/java/carpet/helpers/FeatureGenerator.java
index 98f47007..41228a40 100644
--- a/src/main/java/carpet/helpers/FeatureGenerator.java
+++ b/src/main/java/carpet/helpers/FeatureGenerator.java
@@ -154,10 +154,9 @@ public class FeatureGenerator
return null;
}
- public static BlockBox DEFAULT_BOX = new BlockBox(0,0,0,1,1,1);
- public static BlockBox shouldStructureStartAt(ServerWorld world, BlockPos pos, StructureFeature> structure, boolean computeBox)
+ public static StructureStart shouldStructureStartAt(ServerWorld world, BlockPos pos, StructureFeature> structure, boolean computeBox)
{
- ChunkGenerator generator = world.getChunkManager().getChunkGenerator();
+ ChunkGenerator> generator = world.getChunkManager().getChunkGenerator();
if (!generator.getBiomeSource().hasStructureFeature(structure))
return null;
BiomeAccess biomeAccess = world.getBiomeAccess().withSource(generator.getBiomeSource());
@@ -166,12 +165,12 @@ public class FeatureGenerator
Biome biome = biomeAccess.getBiome(new BlockPos(chunkPos.getStartX() + 9, 0, chunkPos.getStartZ() + 9));
if (structure.method_27217(biomeAccess, generator, chunkRandom, chunkPos.x, chunkPos.z, biome)) // should start at
{
- if (!computeBox) return DEFAULT_BOX;
+ if (!computeBox) return StructureStart.DEFAULT;
StructureManager manager = world.getStructureManager();
StructureStart structureStart3 = structure.getStructureStartFactory().create(structure, chunkPos.x, chunkPos.z, BlockBox.empty(), 0, generator.getSeed());
structureStart3.init(generator, manager, chunkPos.x, chunkPos.z, biome);
if (!structureStart3.hasChildren()) return null;
- return structureStart3.getBoundingBox();
+ return structureStart3;
}
return null;
}
@@ -196,9 +195,9 @@ public class FeatureGenerator
structureToFeature.put(Feature.MINESHAFT.getName(), Arrays.asList("mineshaft", "mineshaft_mesa"));
structureToFeature.put(Feature.IGLOO.getName(), Collections.singletonList("igloo"));
structureToFeature.put(Feature.NETHER_BRIDGE.getName(), Collections.singletonList("fortress"));
- structureToFeature.put("Nether_Fossil", Collections.singletonList("nether_fossil"));
- structureToFeature.put("Bastion_Remnant", Arrays.asList("bastion_remnant", "bastion_remnant_housing", "bastion_remnant_stable", "bastion_remnant_treasure", "bastion_remnant_bridge"));
- structureToFeature.put("Ruined_Portal", Collections.singletonList("ruined_portal"));
+ structureToFeature.put(Feature.NETHER_FOSSIL.getName(), Collections.singletonList("nether_fossil"));
+ structureToFeature.put(Feature.BASTION_REMNANT.getName(), Arrays.asList("bastion_remnant", "bastion_remnant_housing", "bastion_remnant_stable", "bastion_remnant_treasure", "bastion_remnant_bridge"));
+ structureToFeature.put(Feature.RUINED_PORTAL.getName(), Collections.singletonList("ruined_portal"));
structureToFeature.forEach((key, value) -> value.forEach(el -> featureToStructure.put(el, key)));
}
diff --git a/src/main/java/carpet/mixins/PistonBlockEntity_creativeNoClipMixin.java b/src/main/java/carpet/mixins/PistonBlockEntity_creativeNoClipMixin.java
new file mode 100644
index 00000000..9e81659f
--- /dev/null
+++ b/src/main/java/carpet/mixins/PistonBlockEntity_creativeNoClipMixin.java
@@ -0,0 +1,29 @@
+package carpet.mixins;
+
+import carpet.CarpetSettings;
+import net.minecraft.block.entity.PistonBlockEntity;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.util.math.Direction;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(PistonBlockEntity.class)
+public class PistonBlockEntity_creativeNoClipMixin
+{
+ @Inject(method = "method_23672", at = @At("HEAD"), cancellable = true)
+ private static void dontPushSpectators(Direction direction, Entity entity, double d, Direction direction2, CallbackInfo ci)
+ {
+ if (CarpetSettings.creativeNoClip && entity instanceof PlayerEntity && (((PlayerEntity) entity).isCreative()) && ((PlayerEntity) entity).abilities.flying) ci.cancel();
+ }
+
+ @Redirect(method = "pushEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;setVelocity(DDD)V"))
+ private void ignoreAccel(Entity entity, double x, double y, double z)
+ {
+ if (CarpetSettings.creativeNoClip && entity instanceof PlayerEntity && (((PlayerEntity) entity).isCreative()) && ((PlayerEntity) entity).abilities.flying) return;
+ entity.setVelocity(x,y,z);
+ }
+}
diff --git a/src/main/java/carpet/mixins/PlayerEntity_creativeNoClipMixin.java b/src/main/java/carpet/mixins/PlayerEntity_creativeNoClipMixin.java
new file mode 100644
index 00000000..ea50dab4
--- /dev/null
+++ b/src/main/java/carpet/mixins/PlayerEntity_creativeNoClipMixin.java
@@ -0,0 +1,56 @@
+package carpet.mixins;
+
+import carpet.CarpetSettings;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.MovementType;
+import net.minecraft.entity.player.PlayerAbilities;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+@Mixin(PlayerEntity.class)
+public abstract class PlayerEntity_creativeNoClipMixin extends LivingEntity
+{
+ @Shadow public abstract boolean isCreative();
+
+ @Shadow @Final public PlayerAbilities abilities;
+
+ protected PlayerEntity_creativeNoClipMixin(EntityType extends LivingEntity> type, World world)
+ {
+ super(type, world);
+ }
+
+ @Redirect(method = "tick", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/entity/player/PlayerEntity;isSpectator()Z")
+ )
+ private boolean canClipTroughWorld(PlayerEntity playerEntity)
+ {
+ return playerEntity.isSpectator() || (CarpetSettings.creativeNoClip && playerEntity.isCreative() && playerEntity.abilities.flying);
+
+ }
+
+ @Redirect(method = "tickMovement", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/entity/player/PlayerEntity;isSpectator()Z")
+ )
+ private boolean collidesWithEntities(PlayerEntity playerEntity)
+ {
+ return playerEntity.isSpectator() || (CarpetSettings.creativeNoClip && playerEntity.isCreative() && playerEntity.abilities.flying);
+ }
+
+ @Redirect(method = "updateSize", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/entity/player/PlayerEntity;isSpectator()Z")
+ )
+ private boolean spectatorsDontPose(PlayerEntity playerEntity)
+ {
+ return playerEntity.isSpectator() || (CarpetSettings.creativeNoClip && playerEntity.isCreative() && playerEntity.abilities.flying);
+ }
+}
diff --git a/src/main/java/carpet/mixins/WorldRenderer_creativeNoClipMixin.java b/src/main/java/carpet/mixins/WorldRenderer_creativeNoClipMixin.java
new file mode 100644
index 00000000..4e8a8109
--- /dev/null
+++ b/src/main/java/carpet/mixins/WorldRenderer_creativeNoClipMixin.java
@@ -0,0 +1,19 @@
+package carpet.mixins;
+
+import carpet.CarpetSettings;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.render.WorldRenderer;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+@Mixin(WorldRenderer.class)
+public class WorldRenderer_creativeNoClipMixin
+{
+ @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isSpectator()Z"))
+ private boolean canSeeWorld(ClientPlayerEntity clientPlayerEntity)
+ {
+ return clientPlayerEntity.isSpectator() || (CarpetSettings.creativeNoClip && clientPlayerEntity.isCreative());
+ }
+
+}
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index 989cc0f5..0a8cf83b 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -398,6 +398,32 @@ public class CarpetExpression
}
}
+
+
+ private static Value structureToValue(StructureStart structure)
+ {
+ if (structure == null || structure == StructureStart.DEFAULT) return Value.NULL;
+ List pieces = new ArrayList<>();
+ for (StructurePiece piece : structure.getChildren())
+ {
+ BlockBox box = piece.getBoundingBox();
+ pieces.add(ListValue.of(
+ new StringValue( NBTSerializableValue.nameFromRegistryId(Registry.STRUCTURE_PIECE.getId(piece.getType()))),
+ (piece.getFacing()== null)?Value.NULL: new StringValue(piece.getFacing().getName()),
+ ListValue.fromTriple(box.minX, box.minY, box.minZ),
+ ListValue.fromTriple(box.maxX, box.maxY, box.maxZ)
+ ));
+ };
+ BlockBox boundingBox = structure.getBoundingBox();
+ Map ret = new HashMap<>();
+ ret.put(new StringValue("box"), ListValue.of(
+ ListValue.fromTriple(boundingBox.minX, boundingBox.minY, boundingBox.minZ),
+ ListValue.fromTriple(boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ)
+ ));
+ ret.put(new StringValue("pieces"), ListValue.wrap(pieces));
+ return MapValue.wrap(ret);
+ }
+
private void API_BlockManipulation()
{
this.expr.addLazyFunction("block", -1, (c, t, lv) ->
@@ -1148,25 +1174,20 @@ public class CarpetExpression
}
if (structure != null)
{
- BlockBox box = FeatureGenerator.shouldStructureStartAt(world, pos, structure, needSize);
- if (box == null) return LazyValue.NULL;
+ StructureStart start = FeatureGenerator.shouldStructureStartAt(world, pos, structure, needSize);
+ if (start == null) return LazyValue.NULL;
if (!needSize) return LazyValue.TRUE;
- Value ret = ListValue.of(
- ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)),
- ListValue.of(new NumericValue(box.maxX), new NumericValue(box.maxY), new NumericValue(box.maxZ))
- );
+ Value ret = structureToValue(start);
return (_c, _t) -> ret;
}
Map ret = new HashMap<>();
for(StructureFeature> str : Feature.STRUCTURES.values())
{
- BlockBox box = FeatureGenerator.shouldStructureStartAt(world, pos, str, needSize);
- if (box == null) continue;
+ StructureStart start = FeatureGenerator.shouldStructureStartAt(world, pos, str, needSize);
+ if (start == null) continue;
+
Value key = new StringValue(FeatureGenerator.structureToFeature.get(str.getName()).get(0));
- ret.put(key, (!needSize)?Value.NULL:ListValue.of(
- ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)),
- ListValue.of(new NumericValue(box.maxX), new NumericValue(box.maxY), new NumericValue(box.maxZ))
- ));
+ ret.put(key, (!needSize)?Value.NULL:structureToValue(start));
}
Value retMap = MapValue.wrap(ret);
return (_c, _t) -> retMap;
@@ -1188,28 +1209,17 @@ public class CarpetExpression
if (start == StructureStart.DEFAULT)
continue;
BlockBox box = start.getBoundingBox();
- ListValue coord1 = ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ));
- ListValue coord2 = ListValue.of(new NumericValue(box.maxX), new NumericValue(box.maxY), new NumericValue(box.maxZ));
- structureList.put(new StringValue(FeatureGenerator.structureToFeature.get(entry.getKey()).get(0)), ListValue.of(coord1, coord2));
+ structureList.put(
+ new StringValue(FeatureGenerator.structureToFeature.get(entry.getKey()).get(0)),
+ ListValue.of(ListValue.fromTriple(box.minX, box.minY, box.minZ), ListValue.fromTriple(box.maxX, box.maxY, box.maxZ))
+ );
}
Value ret = MapValue.wrap(structureList);
return (_c, _t) -> ret;
}
String structureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT);
StructureStart start = structures.get(FeatureGenerator.featureToStructure.get(structureName));
- if (start == null || start == StructureStart.DEFAULT) return LazyValue.NULL;
- List pieces = new ArrayList<>();
- for (StructurePiece piece : start.getChildren())
- {
- BlockBox box = piece.getBoundingBox();
- pieces.add(ListValue.of(
- new StringValue( NBTSerializableValue.nameFromRegistryId(Registry.STRUCTURE_PIECE.getId(piece.getType()))),
- (piece.getFacing()== null)?Value.NULL: new StringValue(piece.getFacing().getName()),
- ListValue.of(new NumericValue(box.minX), new NumericValue(box.minY), new NumericValue(box.minZ)),
- ListValue.of(new NumericValue(box.maxX), new NumericValue(box.maxY), new NumericValue(box.maxZ))
- ));
- }
- Value ret = ListValue.wrap(pieces);
+ Value ret = structureToValue(start);
return (_c, _t) -> ret;
});
diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java
index 2d1ac5dc..e20d32b3 100644
--- a/src/main/java/carpet/script/value/ListValue.java
+++ b/src/main/java/carpet/script/value/ListValue.java
@@ -75,6 +75,18 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
);
}
+ public static Value fromTriple(double a, double b, double c)
+ {
+ return ListValue.of(new NumericValue(a), new NumericValue(b), new NumericValue(c));
+ }
+
+ public static Value fromTriple(int a, int b, int c)
+ {
+ return fromTriple((double) a, b, c);
+ }
+
+
+
public static ListValue wrap(List list)
{
diff --git a/src/main/resources/assets/carpet/scripts/overlay.sc b/src/main/resources/assets/carpet/scripts/overlay.sc
index 6868fb91..0b501051 100644
--- a/src/main/resources/assets/carpet/scripts/overlay.sc
+++ b/src/main/resources/assets/carpet/scripts/overlay.sc
@@ -93,7 +93,7 @@ __structure_renderer(player_name) ->
for (starts, l(position, name) = _;
structure_data = structures(position):name;
if (!structure_data, continue()); // messed up references - shouldn't happen
- structure_pieces = structures(position, name);
+ structure_pieces = structures(position, name):'pieces';
if (!structure_pieces, continue()); // messed up references - shouldn't happen
l(from, to) = structure_data;
total_size = _euclidean(from, to);
diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json
index 6fd71f94..83bfb498 100644
--- a/src/main/resources/carpet.mixins.json
+++ b/src/main/resources/carpet.mixins.json
@@ -157,7 +157,10 @@
"PistonBlockEntity_movableTEMixin",
"World_movableTEMixin",
"WorldChunk_movableTEMixin",
- "PistonHandler_movableTEMixin"
+ "PistonHandler_movableTEMixin",
+ "PlayerEntity_creativeNoClipMixin",
+ "PistonBlockEntity_creativeNoClipMixin"
+
],
"client": [
"RenderTickCounter_tickSpeedMixin",
@@ -170,7 +173,9 @@
"PistonBlockEntityRenderer_movableTEMixin",
"ClientPlayNetworkHandler_smoothClientAnimationsMixin",
"ClientPlayerEntity_clientCommandMixin",
- "WorldRenderer_fogOffMixin"
+ "WorldRenderer_fogOffMixin",
+ "WorldRenderer_creativeNoClipMixin"
+
],
"server": [
"MinecraftDedicatedServerMixin"