Merge branch 'master' into light

This commit is contained in:
gnembon committed 2020-06-12 00:12:06 -03:00
commit ac9fcd9ece
122 files changed
+7753 -1373

No files matched your search

@@ -6,6 +6,8 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.Map;
public interface CarpetExtension
{
/**
@@ -51,4 +53,6 @@ public interface CarpetExtension
default String version() {return null;}
default void registerLoggers() {}
default Map<String, String> canHasTranslations(String lang) { return null;}
}
+16 -2
View File
@@ -6,6 +6,7 @@ import java.util.List;
import java.util.Random;
import carpet.commands.*;
import carpet.network.ServerNetworkHandler;
import carpet.helpers.TickSpeed;
import carpet.logging.LoggerRegistry;
import carpet.script.CarpetScriptServer;
@@ -56,10 +57,11 @@ public class CarpetServer // static for now - easier to handle all around the co
if (sm != null) sm.attachServer(server);
e.onServerLoaded(server);
});
scriptServer = new CarpetScriptServer();
scriptServer = new CarpetScriptServer(server);
scriptServer.loadAllWorldScripts();
MobAI.resetTrackers();
LoggerRegistry.initLoggers();
//TickSpeed.reset();
}
public static void tick(MinecraftServer server)
@@ -102,29 +104,41 @@ public class CarpetServer // static for now - easier to handle all around the co
public static void onPlayerLoggedIn(ServerPlayerEntity player)
{
ServerNetworkHandler.onPlayerJoin(player);
LoggerRegistry.playerConnected(player);
extensions.forEach(e -> e.onPlayerLoggedIn(player));
}
public static void onPlayerLoggedOut(ServerPlayerEntity player)
{
ServerNetworkHandler.onPlayerLoggedOut(player);
LoggerRegistry.playerDisconnected(player);
extensions.forEach(e -> e.onPlayerLoggedOut(player));
}
public static void onServerClosed(MinecraftServer server)
{
ServerNetworkHandler.close();
currentCommandDispatcher = null;
if (scriptServer != null) scriptServer.onClose();
settingsManager.detachServer();
LoggerRegistry.stopLoggers();
extensions.forEach(e -> e.onServerClosed(server));
minecraft_server = null;
disconnect();
}
public static void registerExtensionLoggers()
{
extensions.forEach(CarpetExtension::registerLoggers);
}
public static void disconnect()
{
// this for whatever reason gets called multiple times even when joining;
TickSpeed.reset();
settingsManager.detachServer();
}
}
+111 -58
View File
@@ -4,14 +4,13 @@ import carpet.settings.ParsedRule;
import carpet.settings.Rule;
import carpet.settings.SettingsManager;
import carpet.settings.Validator;
import carpet.utils.Translations;
import carpet.utils.Messenger;
import carpet.utils.SpawnChunks;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.world.ChunkTicketType;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Unit;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.ChunkPos;
@@ -35,7 +34,7 @@ import static carpet.settings.RuleCategory.CLIENT;
@SuppressWarnings("CanBeFinal")
public class CarpetSettings
{
public static final String carpetVersion = "1.3.17+v200401";
public static final String carpetVersion = "1.3.29+v200611";
public static final Logger LOG = LogManager.getLogger();
public static boolean skipGenerationChecks = false;
public static boolean impendingFillSkipUpdates = false;
@@ -43,6 +42,31 @@ public class CarpetSettings
public static Vec3d fixedPosition = null;
public static int runPermissionLevel = 2;
private static class LanguageValidator extends Validator<String> {
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
if (currentRule.get().equals(newValue) || source == null)
{
return newValue;
}
if (!Translations.isValidLanguage(newValue))
{
Messenger.m(source, "r "+newValue+" is not a valid language");
return null;
}
CarpetSettings.language = newValue;
Translations.updateLanguage(source);
return newValue;
}
}
@Rule(
desc = "sets the language for carpet",
category = FEATURE,
options = {"none", "zh_cn", "zh_tw"},
strict = false,
validate = LanguageValidator.class
)
public static String language = "none";
@Rule(
desc = "Nether portals correctly place entities going through",
extra = "Entities shouldn't suffocate in obsidian",
@@ -71,6 +95,7 @@ public class CarpetSettings
)
public static int portalSurvivalDelay = 80;
private static class OneHourMaxDelayLimit extends Validator<Integer> {
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string) {
return (newValue > 0 && newValue <= 72000) ? newValue : null;
@@ -236,13 +261,6 @@ public class CarpetSettings
@Rule( desc = "Pistons can push block entities, like hoppers, chests etc.", category = {EXPERIMENTAL, FEATURE} )
public static boolean movableBlockEntities = false;
/*@Rule(
desc = "Orange stained glass behaves like slime, but doesn't stick to it",
extra = "Only available for 1.14. in 1.15 use honey instead",
category = {EXPERIMENTAL, FEATURE}
)
public static boolean honeySlime = false;*/
@Rule( desc = "Saplings turn into dead shrubs in hot climates and no water access", category = FEATURE )
public static boolean desertShrubs = false;
@@ -308,7 +326,7 @@ public class CarpetSettings
)
public static String commandPerimeterInfo = "true";
@Rule(desc = "Enables /draw commands", extra = "... allows for drawing simple shapes", category = COMMAND)
@Rule(desc = "Enables /draw commands", extra = {"... allows for drawing simple shapes or","other shapes which are sorta difficult to do normally"}, category = COMMAND)
public static String commandDraw = "true";
@Rule(
@@ -356,9 +374,6 @@ public class CarpetSettings
@Rule(desc = "Pistons, Glass and Sponge can be broken faster with their appropriate tools", category = SURVIVAL)
public static boolean missingTools = false;
//@Rule(desc = "Alternative, persistent caching strategy for nether portals", category = {SURVIVAL, CREATIVE})
//public static boolean portalCaching = false; // ineffective in 1.15
@Rule(desc = "fill/clone/setblock and structure blocks cause block updates", category = CREATIVE)
public static boolean fillUpdates = true;
@@ -472,7 +487,7 @@ public class CarpetSettings
{
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string)
{
if (currentRule.get().equals(newValue))
if (currentRule.get().equals(newValue) || source == null)
{
return newValue;
}
@@ -486,8 +501,8 @@ public class CarpetSettings
if (server.isDedicated())
{
int vd = (newValue >= 2)?newValue:((DedicatedServer) server).getProperties().viewDistance;
if (vd != CarpetServer.minecraft_server.getPlayerManager().getViewDistance())
CarpetServer.minecraft_server.getPlayerManager().setViewDistance(vd);
if (vd != server.getPlayerManager().getViewDistance())
server.getPlayerManager().setViewDistance(vd);
return newValue;
}
else
@@ -509,13 +524,9 @@ public class CarpetSettings
)
public static int viewDistance = 0;
private static class DisableSpawnChunksValidator extends Validator<Boolean> {
@Override public Boolean validate(ServerCommandSource source, ParsedRule<Boolean> currentRule, Boolean newValue, String string) {
if (currentRule.get().booleanValue() == newValue.booleanValue())
{
//must been some startup thing
return newValue;
}
public static class ChangeSpawnChunksValidator extends Validator<Integer> {
public static void changeSpawnSize(int size)
{
ServerWorld overworld = CarpetServer.minecraft_server.getWorld(DimensionType.OVERWORLD);
if (overworld != null) {
ChunkPos centerChunk = new ChunkPos(new BlockPos(
@@ -523,40 +534,39 @@ public class CarpetSettings
overworld.getLevelProperties().getSpawnY(),
overworld.getLevelProperties().getSpawnZ()
));
ServerChunkManager chunkManager = (ServerChunkManager) overworld.getChunkManager();
chunkManager.removeTicket(ChunkTicketType.START, centerChunk, 11, Unit.INSTANCE);
if (!newValue)
{
chunkManager.addTicket(ChunkTicketType.START, centerChunk, 11, Unit.INSTANCE);
}
SpawnChunks.changeSpawnChunks(overworld.getChunkManager(), centerChunk, size);
}
}
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string) {
if (source == null) return newValue;
if (newValue < 0 || newValue > 32)
{
Messenger.m(source, "r spawn chunk size has to be between 0 and 32");
return null;
}
if (currentRule.get().intValue() == newValue.intValue())
{
//must been some startup thing
return newValue;
}
if (CarpetServer.minecraft_server == null) return newValue;
ServerWorld currentOverworld = CarpetServer.minecraft_server.getWorld(DimensionType.OVERWORLD);
if (currentOverworld != null)
{
changeSpawnSize(newValue);
}
return newValue;
}
}
@Rule(
desc = "Allows spawn chunks to unload",
desc = "Changes size of spawn chunks",
extra = {"Defines new radius", "setting it to 0 - disables spawn chunks"},
category = CREATIVE,
validate = DisableSpawnChunksValidator.class
)
public static boolean disableSpawnChunks = false;
/*private static class KelpLimit extends Validator<Integer>
{
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string) {
return (newValue>=0 && newValue <=25)?newValue:null;
}
@Override
public String description() { return "You must choose a value from 0 to 25. 25 and all natural kelp can grow 25 blocks, choose 0 to make all generated kelp not to grow";}
}
@Rule(
desc = "limits growth limit of newly naturally generated kelp to this amount of blocks",
options = {"0", "2", "25"},
category = FEATURE,
strict = false,
validate = KelpLimit.class
options = {"0", "11"},
validate = ChangeSpawnChunksValidator.class
)
public static int kelpGenerationGrowthLimit = 25;*/
public static int spawnChunksSize = 11;
@Rule(desc = "Coral structures will grow with bonemeal from coral plants", category = FEATURE)
public static boolean renewableCoral = false;
@@ -573,13 +583,6 @@ public class CarpetSettings
@Rule(desc = "Spawning requires much less CPU and Memory", category = OPTIMIZATION)
public static boolean lagFreeSpawning = false;
//@Rule(
// desc = "Prevents horses and other mobs to wander into the distance after dismounting",
// extra = "Fixes issues with various Joergens wandering off and disappearing client-side",
// category = BUGFIX
//)
//public static boolean horseWanderingFix = false;
@Rule(
desc = "Allows structure mobs to spawn in flat worlds",
@@ -599,4 +602,54 @@ public class CarpetSettings
category = CREATIVE
)
public static boolean extremeBehaviours = false;
@Rule(
desc = "Removes fog from client in the nether and the end",
extra = "Improves visibility, but looks weird",
category = CLIENT
)
public static boolean fogOff = false;
@Rule(
desc = "Creative No Clip",
extra = {
"On servers it needs to be set on both ",
"client and server to function properly.",
"Has no effect when set on the server only",
"Can allow to phase through walls",
"if only set on the carpet client side",
"but requires some trapdoor magic to",
"allow the player to enter blocks"
},
category = {CREATIVE, CLIENT}
)
public static boolean creativeNoClip = false;
@Rule(
desc = "Creative flying speed multiplier",
extra = {
"Purely client side setting, meaning that",
"having it set on the decicated server has no effect",
"but this also means it will work on vanilla servers as well"
},
category = {CREATIVE, CLIENT},
validate = Validator.NONNEGATIVE_NUMBER.class
)
public static double creativeFlySpeed = 1.0;
@Rule(
desc = "Creative air drag",
extra = {
"Increased drag will slow down your flight",
"So need to adjust speed accordingly",
"With 1.0 drag, using speed of 11 seems to matching vanilla speeds.",
"Purely client side setting, meaning that",
"having it set on the decicated server has no effect",
"but this also means it will work on vanilla servers as well"
},
category = {CREATIVE, CLIENT},
validate = Validator.PROBABILITY.class
)
public static double creativeFlyDrag = 0.09;
}
+359 -68
View File
@@ -7,6 +7,11 @@ import com.google.common.collect.Lists;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.Block;
import net.minecraft.block.pattern.CachedBlockPosition;
import net.minecraft.server.command.ServerCommandSource;
@@ -18,12 +23,15 @@ import net.minecraft.inventory.Inventory;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.MathHelper;
import java.util.List;
import java.util.function.Predicate;
import java.lang.Math;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
import static net.minecraft.server.command.CommandSource.suggestMatching;
public class DrawCommand
{
@@ -32,45 +40,142 @@ public class DrawCommand
LiteralArgumentBuilder<ServerCommandSource> command = literal("draw").
requires((player) -> SettingsManager.canUseCommand(player, CarpetSettings.commandDraw)).
then(literal("sphere").
then(argument("center",BlockPosArgumentType.blockPos()).
then(argument("radius",IntegerArgumentType.integer(1)).
then(argument("block",BlockStateArgumentType.blockState()).
executes((c)-> drawCircle(
c.getSource(),
BlockPosArgumentType.getBlockPos(c, "center"),
IntegerArgumentType.getInteger(c, "radius"),
BlockStateArgumentType.getBlockState (c,"block"),
null
)
).
then(literal("replace").
then(argument("filter",BlockPredicateArgumentType.blockPredicate())
.executes((c)-> drawCircle(
c.getSource(),
BlockPosArgumentType.getBlockPos(c, "center"),
IntegerArgumentType.getInteger(c, "radius"),
BlockStateArgumentType.getBlockState(c,"block"),
BlockPredicateArgumentType.getBlockPredicate(c,
"filter")
))))))));
then(argument("center", BlockPosArgumentType.blockPos()).
then(argument("radius", IntegerArgumentType.integer(1)).
then(drawShape(c -> DrawCommand.drawSphere(c, false)))))).
then(literal("ball").
then(argument("center", BlockPosArgumentType.blockPos()).
then(argument("radius", IntegerArgumentType.integer(1)).
then(drawShape(c -> DrawCommand.drawSphere(c, true)))))).
then(literal("diamond").
then(argument("center", BlockPosArgumentType.blockPos()).
then(argument("radius", IntegerArgumentType.integer(1)).
then(drawShape(c -> DrawCommand.drawDiamond(c, true)))))).
then(literal("pyramid").
then(argument("center", BlockPosArgumentType.blockPos()).
then(argument("radius", IntegerArgumentType.integer(1)).
then(argument("height",IntegerArgumentType.integer(1)).
then(argument("pointing",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"up","down"},b)).
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b)).
then(drawShape(c -> DrawCommand.drawPyramid(c, "square", true))))))))).
then(literal("cone").
then(argument("center", BlockPosArgumentType.blockPos()).
then(argument("radius", IntegerArgumentType.integer(1)).
then(argument("height",IntegerArgumentType.integer(1)).
then(argument("pointing",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"up","down"},b)).
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b))
.then(drawShape(c -> DrawCommand.drawPyramid(c, "circle", true))))))))).
then(literal("cylinder").
then(argument("center", BlockPosArgumentType.blockPos()).
then(argument("radius", IntegerArgumentType.integer(1)).
then(argument("height",IntegerArgumentType.integer(1)).
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b))
.then(drawShape(c -> DrawCommand.drawPrism(c, "circle")))))))).
then(literal("cuboid").
then(argument("center", BlockPosArgumentType.blockPos()).
then(argument("radius", IntegerArgumentType.integer(1)).
then(argument("height",IntegerArgumentType.integer(1)).
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b))
.then(drawShape(c -> DrawCommand.drawPrism(c, "square"))))))));
dispatcher.register(command);
}
private static int drawCircle(ServerCommandSource source, BlockPos pos, int radius, BlockStateArgument block,
Predicate<CachedBlockPosition> replacement)
@FunctionalInterface
private interface OptionalBlockSelector
{
return drawCircle(source, pos, (double)radius, (double)radius, (double)radius, block, replacement, false);
Integer apply(final CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException;
}
private static int drawCircle(ServerCommandSource source, BlockPos pos, double radiusX, double radiusY, double radiusZ,
BlockStateArgument block, Predicate<CachedBlockPosition> replacement, boolean solid)
{
int affected = 0;
ServerWorld world = source.getWorld();
radiusX += 0.5;
radiusY += 0.5;
radiusZ += 0.5;
@FunctionalInterface
private interface ArgumentExtractor<T>
{
T apply(final CommandContext<ServerCommandSource> ctx, final String argName) throws CommandSyntaxException;
}
private static RequiredArgumentBuilder<ServerCommandSource, BlockStateArgument>
drawShape(OptionalBlockSelector drawer)
{
return argument("block", BlockStateArgumentType.blockState()).
executes(drawer::apply)
.then(literal("replace")
.then(argument("filter", BlockPredicateArgumentType.blockPredicate())
.executes(drawer::apply)));
}
private static class ErrorHandled extends RuntimeException {}
private static <T> T getArg(CommandContext<ServerCommandSource> ctx, ArgumentExtractor<T> extract, String hwat) throws CommandSyntaxException
{
return getArg(ctx, extract, hwat, false);
}
private static <T> T getArg(CommandContext<ServerCommandSource> ctx, ArgumentExtractor<T> extract, String hwat, boolean optional) throws CommandSyntaxException
{
T arg = null;
try
{
arg = extract.apply(ctx, hwat);
}
catch (IllegalArgumentException e)
{
if (optional) return null;
Messenger.m(ctx.getSource(), "rb Missing "+hwat);
throw new ErrorHandled();
}
return arg;
}
private static double lengthSq(double x, double y, double z)
{
return (x * x) + (y * y) + (z * z);
}
private static int setBlock(
ServerWorld world, BlockPos.Mutable mbpos, int x, int y, int z,
BlockStateArgument block, Predicate<CachedBlockPosition> replacement,
List<BlockPos> list
)
{
mbpos.set(x, y, z);
int success=0;
if (replacement == null || replacement.test(new CachedBlockPosition(world, mbpos, true)))
{
BlockEntity tileentity = world.getBlockEntity(mbpos);
if (tileentity instanceof Inventory)
{
((Inventory) tileentity).clear();
}
if (block.setBlockState(world, mbpos, 2))
{
list.add(mbpos.toImmutable());
++success;
}
}
return success;
}
private static int drawSphere(CommandContext<ServerCommandSource> ctx, boolean solid) throws CommandSyntaxException
{
BlockPos pos;
int radius;
BlockStateArgument block;
Predicate<CachedBlockPosition> replacement;
try
{
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius");
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
}
catch (ErrorHandled ignored) { return 0; }
int affected = 0;
ServerWorld world = ctx.getSource().getWorld();
double radiusX = radius+0.5;
double radiusY = radius+0.5;
double radiusZ = radius+0.5;
final double invRadiusX = 1 / radiusX;
final double invRadiusY = 1 / radiusY;
@@ -85,22 +190,28 @@ public class DrawCommand
double nextXn = 0;
forX: for (int x = 0; x <= ceilRadiusX; ++x) {
forX: for (int x = 0; x <= ceilRadiusX; ++x)
{
final double xn = nextXn;
nextXn = (x + 1) * invRadiusX;
double nextYn = 0;
forY: for (int y = 0; y <= ceilRadiusY; ++y) {
forY: for (int y = 0; y <= ceilRadiusY; ++y)
{
final double yn = nextYn;
nextYn = (y + 1) * invRadiusY;
double nextZn = 0;
forZ: for (int z = 0; z <= ceilRadiusZ; ++z) {
forZ: for (int z = 0; z <= ceilRadiusZ; ++z)
{
final double zn = nextZn;
nextZn = (z + 1) * invRadiusZ;
double distanceSq = lengthSq(xn, yn, zn);
if (distanceSq > 1) {
if (z == 0) {
if (y == 0) {
if (distanceSq > 1)
{
if (z == 0)
{
if (y == 0)
{
break forX;
}
break forY;
@@ -108,35 +219,22 @@ public class DrawCommand
break forZ;
}
if (!solid) {
if (lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1) {
continue;
}
if (!solid && lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1)
{
continue;
}
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
for (int xmod = -1; xmod < 2; xmod+= 2)
for (int xmod = -1; xmod < 2; xmod += 2)
{
for (int ymod = -1; ymod < 2; ymod += 2)
{
for (int zmod = -1; zmod < 2; zmod += 2)
{
mbpos.set(pos.getX()+xmod*x, pos.getY()+ymod*y, pos.getZ()+zmod*z);
if (replacement == null || replacement.test(
new CachedBlockPosition( world, mbpos, true)))
{
BlockEntity tileentity = world.getBlockEntity(mbpos);
if (tileentity instanceof Inventory)
{
((Inventory)tileentity).clear();
}
if (block.setBlockState(world, mbpos,2))
{
list.add(mbpos.toImmutable());
++affected;
}
}
affected+= setBlock(world, mbpos,
pos.getX() + xmod * x, pos.getY() + ymod * y, pos.getZ() + zmod * z,
block, replacement, list
);
}
}
}
@@ -146,19 +244,212 @@ public class DrawCommand
}
if (CarpetSettings.fillUpdates)
{
list.forEach(blockpos1 -> world.updateNeighbors(blockpos1, world.getBlockState(blockpos1).getBlock()));
}
Messenger.m(ctx.getSource(), "gi Filled " + affected + " blocks");
return affected;
}
for (BlockPos blockpos1 : list)
private static int drawDiamond(CommandContext<ServerCommandSource> ctx, boolean solid) throws CommandSyntaxException
{
BlockPos pos;
int radius;
BlockStateArgument block;
Predicate<CachedBlockPosition> replacement;
try
{
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius");
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
}
catch (ErrorHandled ignored) { return 0; }
ServerCommandSource source = ctx.getSource();
int affected=0;
BlockPos.Mutable mbpos = new BlockPos.Mutable(pos);
List<BlockPos> list = Lists.newArrayList();
ServerWorld world = source.getWorld();
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
for (int r = 0; r < radius; ++r)
{
int y=r-radius+1;
for (int x = -r; x <= r; ++x)
{
Block blokc = world.getBlockState(blockpos1).getBlock();
world.updateNeighbors(blockpos1, blokc); // or always version????
int z=r-Math.abs(x);
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()-y, pos.getZ()+z, block, replacement, list);
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()-y, pos.getZ()-z, block, replacement, list);
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()+y, pos.getZ()+z, block, replacement, list);
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()+y, pos.getZ()-z, block, replacement, list);
}
}
Messenger.m(source, "gi Filled "+affected+" blocks");
return 1;
}
private static double lengthSq(double x, double y, double z)
CarpetSettings.impendingFillSkipUpdates = false;
if (CarpetSettings.fillUpdates)
{
return (x * x) + (y * y) + (z * z);
list.forEach(p -> world.updateNeighbors(p, world.getBlockState(p).getBlock()));
}
Messenger.m(source, "gi Filled " + affected + " blocks");
return affected;
}
private static int fillFlat(
ServerWorld world, BlockPos pos, int offset, double dr, boolean rectangle, String orientation,
BlockStateArgument block, Predicate<CachedBlockPosition> replacement,
List<BlockPos> list, BlockPos.Mutable mbpos
)
{
int successes=0;
int r = MathHelper.floor(dr);
double drsq = dr*dr;
if (orientation.equalsIgnoreCase("x"))
{
for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq)
{
successes += setBlock(
world, mbpos,pos.getX()+offset, pos.getY()+a, pos.getZ()+b,
block, replacement, list
);
}
return successes;
}
if (orientation.equalsIgnoreCase("y"))
{
for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq)
{
successes += setBlock(
world, mbpos,pos.getX()+a, pos.getY()+offset, pos.getZ()+b,
block, replacement, list
);
}
return successes;
}
if (orientation.equalsIgnoreCase("z"))
{
for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq)
{
successes += setBlock(
world, mbpos,pos.getX()+b, pos.getY()+a, pos.getZ()+offset,
block, replacement, list
);
}
return successes;
}
return 0;
}
private static int drawPyramid(CommandContext<ServerCommandSource> ctx, String base, boolean solid) throws CommandSyntaxException
{
BlockPos pos;
double radius;
int height;
boolean pointup;
String orientation;
BlockStateArgument block;
Predicate<CachedBlockPosition> replacement;
try
{
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius")+0.5D;
height = getArg(ctx, IntegerArgumentType::getInteger, "height");
pointup = getArg(ctx, StringArgumentType::getString, "pointing").equalsIgnoreCase("up");
orientation = getArg(ctx, StringArgumentType::getString,"orientation");
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
}
catch (ErrorHandled ignored) { return 0; }
ServerCommandSource source = ctx.getSource();
int affected = 0;
BlockPos.Mutable mbpos = new BlockPos.Mutable(pos);
List<BlockPos> list = Lists.newArrayList();
ServerWorld world = source.getWorld();
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
boolean isSquare = base.equalsIgnoreCase("square");
for(int i =0; i<height;++i)
{
double r = pointup ? radius - radius * i / height - 1 : radius * i / height;
affected+= fillFlat(world, pos, i, r, isSquare, orientation, block, replacement, list, mbpos);
}
CarpetSettings.impendingFillSkipUpdates = false;
if (CarpetSettings.fillUpdates) {
for (BlockPos blockpos1 : list) {
Block blokc = world.getBlockState(blockpos1).getBlock();
world.updateNeighbors(blockpos1, blokc);
}
}
Messenger.m(source, "gi Filled " + affected + " blocks");
return affected;
}
private static int drawPrism(CommandContext<ServerCommandSource> ctx, String base){
BlockPos pos;
double radius;
int height;
String orientation;
BlockStateArgument block;
Predicate<CachedBlockPosition> replacement;
try
{
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius")+0.5D;
height = getArg(ctx, IntegerArgumentType::getInteger, "height");
orientation = getArg(ctx, StringArgumentType::getString,"orientation");
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
}
catch (ErrorHandled | CommandSyntaxException ignored) { return 0; }
ServerCommandSource source = ctx.getSource();
int affected = 0;
BlockPos.Mutable mbpos = new BlockPos.Mutable(pos);
List<BlockPos> list = Lists.newArrayList();
ServerWorld world = source.getWorld();
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
boolean isSquare = base.equalsIgnoreCase("square");
for(int i =0; i<height;++i)
{
affected+= fillFlat(world, pos, i, radius, isSquare, orientation, block, replacement, list, mbpos);
}
CarpetSettings.impendingFillSkipUpdates = false;
if (CarpetSettings.fillUpdates) {
for (BlockPos blockpos1 : list) {
Block blokc = world.getBlockState(blockpos1).getBlock();
world.updateNeighbors(blockpos1, blokc);
}
}
Messenger.m(source, "gi Filled " + affected + " blocks");
return affected;
}
}
@@ -296,6 +296,14 @@ public class PlayerCommand
Messenger.m(context.getSource(), "r Cannot shadow fake players");
return 0;
}
ServerPlayerEntity sendingPlayer = null;
try
{
sendingPlayer = context.getSource().getPlayer();
}
catch (CommandSyntaxException ignored) { }
if (sendingPlayer!=player && cantManipulate(context)) return 0;
EntityPlayerMPFake.createShadow(player.server, player);
return 1;
}
@@ -16,6 +16,7 @@ import net.minecraft.command.arguments.DimensionArgumentType;
import net.minecraft.entity.EntityCategory;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.DyeColor;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.dimension.DimensionType;
@@ -98,7 +99,8 @@ public class SpawnCommand
executes( (c) -> generalMobcaps(c.getSource()) ).
then(argument("type", string()).
suggests( (c, b)->suggestMatching(Arrays.stream(EntityCategory.values()).map(EntityCategory::getName), b)).
executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type")))));
executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type"), false)).
then(literal("all").executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type"), true)))));
dispatcher.register(literalargumentbuilder);
}
@@ -194,7 +196,7 @@ public class SpawnCommand
TickSpeed.tickrate_advance(null, 0, null, null);
// tick warp given player
ServerCommandSource csource = null;
PlayerEntity player = null;
ServerPlayerEntity player = null;
try
{
player = source.getPlayer();
@@ -262,10 +264,10 @@ public class SpawnCommand
return 1;
}
private static int listEntitiesOfType(ServerCommandSource source, String mobtype) throws CommandSyntaxException
private static int listEntitiesOfType(ServerCommandSource source, String mobtype, boolean all) throws CommandSyntaxException
{
EntityCategory cat = getCategory(mobtype);
Messenger.send(source, SpawnReporter.printEntitiesByType(cat, source.getWorld()));
Messenger.send(source, SpawnReporter.printEntitiesByType(cat, source.getWorld(), all));
return 1;
}
}
+10 -5
View File
@@ -9,6 +9,7 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.BaseText;
import net.minecraft.server.command.ServerCommandSource;
@@ -43,7 +44,8 @@ public class TickCommand
c.getSource(),
getInteger(c,"ticks"),
getString(c, "tail command")))))).
then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource()))).
then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource(), false)).
then(literal("deep").executes( (c)-> toggleFreeze(c.getSource(), true)))).
then(literal("step").
executes((c) -> step(1)).
then(argument("ticks", integer(1,72000)).
@@ -66,7 +68,7 @@ public class TickCommand
private static int setTps(ServerCommandSource source, float tps)
{
TickSpeed.tickrate(tps);
TickSpeed.tickrate(tps, true);
queryTps(source);
return (int)tps;
}
@@ -79,7 +81,7 @@ public class TickCommand
private static int setWarp(ServerCommandSource source, int advance, String tail_command)
{
PlayerEntity player = null;
ServerPlayerEntity player = null;
try
{
player = source.getPlayer();
@@ -95,15 +97,18 @@ public class TickCommand
return 1;
}
private static int toggleFreeze(ServerCommandSource source)
private static int toggleFreeze(ServerCommandSource source, boolean isDeep)
{
TickSpeed.is_paused = !TickSpeed.is_paused;
if (TickSpeed.is_paused)
{
Messenger.m(source, "gi Game is paused");
TickSpeed.deepFreeze = isDeep;
Messenger.m(source, "gi Game is "+(isDeep?"deeply ":"")+"frozen");
}
else
{
TickSpeed.deepFreeze = false;
Messenger.m(source, "gi Game runs normally");
}
return 1;
@@ -0,0 +1,12 @@
package carpet.fakes;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.server.world.ChunkTicket;
import net.minecraft.util.SortedArraySet;
import net.minecraft.util.math.ChunkPos;
public interface ChunkTicketManagerInterface
{
void changeSpawnChunks(ChunkPos pos, int distance);
Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition();
}
@@ -0,0 +1,10 @@
package carpet.fakes;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
public interface CustomPayloadC2SPacketInterface
{
Identifier getPacketChannel();
PacketByteBuf getPacketData();
}
@@ -11,4 +11,8 @@ public interface EntityInterface
boolean isPermanentVehicle();
void setPermanentVehicle(boolean permanent);
int getPortalTimer();
void setPortalTimer(int amount);
}
@@ -0,0 +1,6 @@
package carpet.fakes;
public interface HungerManagerInterface {
float getExhaustionCM();
void setExhaustionCM(float exhaust);
}
@@ -2,6 +2,6 @@ package carpet.fakes;
public interface ItemEntityInterface
{
int getAge();
int getPickupDelay();
int getAgeCM();
int getPickupDelayCM();
}
@@ -0,0 +1,7 @@
package carpet.fakes;
public interface LivingEntityInterface
{
void doJumpCM(); // added CM suffix to remove potential collisions with other mods
boolean isJumpingCM();
}
@@ -0,0 +1,8 @@
package carpet.fakes;
import net.minecraft.server.world.ChunkTicketManager;
public interface ServerChunkManagerInterface
{
ChunkTicketManager getCMTicketManager();
}
@@ -263,6 +263,15 @@ public class BlockRotator
}
world.setBlockState(blockpos, iblockstate.with(HorizontalFacingBlock.FACING, face), 3);
}
else if(block == Blocks.HOPPER )
{
Direction face = iblockstate.get(HopperBlock.FACING);
if (face != Direction.DOWN)
{
face = rotateClockwise(face, Direction.Axis.Y);
world.setBlockState(blockpos, iblockstate.with(HopperBlock.FACING, face), 3);
}
}
// Send block update to the block that just have been rotated.
world.updateNeighbor(blockpos, block, source.getBlockPos());
@@ -270,8 +279,6 @@ public class BlockRotator
}
public static boolean flip_block(BlockState state, World world, PlayerEntity player, Hand hand, BlockHitResult hit)
{
Block block = state.getBlock();
@@ -390,6 +390,7 @@ public class EntityPlayerActionPack
{
player.interactionManager.processBlockBreakingAction(pos, PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, side, player.server.getWorldHeight());
ap.currentBlock = null;
ap.blockHitDelay = 5;
blockBroken = true;
}
player.world.setBlockBreakingInfo(-1, pos, (int) (ap.curBlockDamageMP * 10));
@@ -6,13 +6,19 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import net.minecraft.block.Blocks;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.structure.StructureManager;
import net.minecraft.structure.StructureStart;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.biome.DefaultBiomeFeatures;
import net.minecraft.world.biome.source.BiomeAccess;
import net.minecraft.world.biome.source.BiomeSource;
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
import net.minecraft.world.biome.source.VanillaLayeredBiomeSourceConfig;
import net.minecraft.world.gen.ChunkRandom;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.chunk.OverworldChunkGenerator;
import net.minecraft.world.gen.chunk.OverworldChunkGeneratorConfig;
@@ -141,26 +147,47 @@ public class FeatureGenerator
return null;
}
public static StructureStart shouldStructureStartAt(ServerWorld world, BlockPos pos, StructureFeature<?> structure, boolean computeBox)
{
ChunkGenerator<?> generator = world.getChunkManager().getChunkGenerator();
if (!generator.getBiomeSource().hasStructureFeature(structure))
return null;
BiomeAccess biomeAccess = world.getBiomeAccess().withSource(generator.getBiomeSource());
ChunkRandom chunkRandom = new ChunkRandom();
ChunkPos chunkPos = new ChunkPos(pos);
Biome biome = biomeAccess.getBiome(new BlockPos(chunkPos.getStartX() + 9, 0, chunkPos.getStartZ() + 9));
if (structure.shouldStartAt(biomeAccess, generator, chunkRandom, chunkPos.x, chunkPos.z, biome))
{
if (!computeBox) return StructureStart.DEFAULT;
StructureManager manager = world.getSaveHandler().getStructureManager();
StructureStart structureStart3 = structure.getStructureStartFactory().create(structure, chunkPos.x, chunkPos.z, BlockBox.empty(), 0, generator.getSeed());
structureStart3.initialize(generator, manager, chunkPos.x, chunkPos.z, biome);
if (!structureStart3.hasChildren()) return null;
return structureStart3;
}
return null;
}
public static final Map<String, List<String>> structureToFeature = new HashMap<>();
public static final Map<String, String> featureToStructure = new HashMap<>();
static
{
structureToFeature.put("Monument", Collections.singletonList("monument"));
structureToFeature.put("EndCity", Collections.singletonList("end_city"));
structureToFeature.put("Ocean_Ruin", Arrays.asList("ocean_ruin", "ocean_ruin_warm", "ocean_ruin_small", "ocean_ruin_warm_small", "ocean_ruin_tall", "ocean_ruin_warm_tall"));
structureToFeature.put("Village", Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy"));
structureToFeature.put("Mansion", Collections.singletonList("mansion"));
structureToFeature.put("Buried_Treasure", Collections.singletonList("treasure"));
structureToFeature.put("Swamp_Hut", Collections.singletonList("witch_hut"));
structureToFeature.put("Stronghold", Collections.singletonList("stronghold"));
structureToFeature.put("Desert_Pyramid", Collections.singletonList("desert_temple"));
structureToFeature.put("Jungle_Pyramid", Collections.singletonList("jungle_temple"));
structureToFeature.put("Shipwreck", Arrays.asList("shipwreck", "shipwreck2"));
structureToFeature.put("Pillager_Outpost", Collections.singletonList("pillager_outpost"));
structureToFeature.put("Mineshaft", Arrays.asList("mineshaft", "mineshaft_mesa"));
structureToFeature.put("Igloo", Collections.singletonList("igloo"));
structureToFeature.put("Fortress", Collections.singletonList("fortress"));
structureToFeature.put(Feature.OCEAN_MONUMENT.getName(), Collections.singletonList("monument"));
structureToFeature.put(Feature.END_CITY.getName(), Collections.singletonList("end_city"));
structureToFeature.put(Feature.OCEAN_RUIN.getName(), Arrays.asList("ocean_ruin", "ocean_ruin_warm", "ocean_ruin_small", "ocean_ruin_warm_small", "ocean_ruin_tall", "ocean_ruin_warm_tall"));
structureToFeature.put(Feature.VILLAGE.getName(), Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy"));
structureToFeature.put(Feature.WOODLAND_MANSION.getName(), Collections.singletonList("mansion"));
structureToFeature.put(Feature.BURIED_TREASURE.getName(), Collections.singletonList("treasure"));
structureToFeature.put(Feature.SWAMP_HUT.getName(), Collections.singletonList("witch_hut"));
structureToFeature.put(Feature.STRONGHOLD.getName(), Collections.singletonList("stronghold"));
structureToFeature.put(Feature.DESERT_PYRAMID.getName(), Collections.singletonList("desert_temple"));
structureToFeature.put(Feature.JUNGLE_TEMPLE.getName(), Collections.singletonList("jungle_temple"));
structureToFeature.put(Feature.SHIPWRECK.getName(), Arrays.asList("shipwreck", "shipwreck2"));
structureToFeature.put(Feature.PILLAGER_OUTPOST.getName(), Collections.singletonList("pillager_outpost"));
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.forEach((key, value) -> value.forEach(el -> featureToStructure.put(el, key)));
}
@@ -126,7 +126,7 @@ public class HopperCounter
color, ticks*1.0/(20*60), (realTime?" - real time":""), total, total*1.0*(20*60*60)/ticks),
"nb [X]", "^g reset", "!/counter "+color+" reset"
));
items.addAll(counter.object2LongEntrySet().stream().map(e ->
items.addAll(counter.object2LongEntrySet().stream().sorted((e, f) -> Long.compare(f.getLongValue(), e.getLongValue())).map(e ->
{
BaseText itemName = new TranslatableText(e.getKey().getTranslationKey());
long count = e.getLongValue();
@@ -7,8 +7,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import carpet.logging.logHelpers.ExplosionLogHelper;
import carpet.mixins.ExplosionAccessor;
import carpet.CarpetSettings;
import carpet.mixins.ExplosionMixin;
import carpet.utils.Messenger;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
@@ -51,6 +53,7 @@ public class OptimizedExplosion
private static List<Entity> entitylist;
private static Vec3d vec3dmem;
private static long tickmem;
private static Vec3d vec3dZero = new Vec3d(0, 0, 0);
// For disabling the explosion particles and sound
public static int explosionSound = 0;
@@ -66,7 +69,7 @@ public class OptimizedExplosion
private static ArrayList<Float> chances = new ArrayList<>();
private static BlockPos blastChanceLocation;
public static void doExplosionA(Explosion e) {
public static void doExplosionA(Explosion e, ExplosionLogHelper eLogger) {
ExplosionAccessor eAccess = (ExplosionAccessor) e;
blastCalc(e);
@@ -120,6 +123,9 @@ public class OptimizedExplosion
entity.getX() == eAccess.getEntity().getX() &&
entity.getY() == eAccess.getEntity().getY() &&
entity.getZ() == eAccess.getEntity().getZ()) {
if (eLogger != null) {
eLogger.onEntityImpacted(entity, new Vec3d(0,-0.9923437498509884d, 0));
}
continue;
}
@@ -158,6 +164,10 @@ public class OptimizedExplosion
d11 = ProtectionEnchantment.transformExplosionKnockback((LivingEntity) entity, d10);
}
if (eLogger != null) {
eLogger.onEntityImpacted(entity, new Vec3d(d5 * d11, d7 * d11, d9 * d11));
}
entity.setVelocity(entity.getVelocity().add(d5 * d11, d7 * d11, d9 * d11));
if (entity instanceof PlayerEntity) {
+30 -7
View File
@@ -5,11 +5,12 @@ import java.util.Map;
import java.util.function.BiConsumer;
import carpet.CarpetServer;
import carpet.network.ServerNetworkHandler;
import carpet.utils.Messenger;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.BaseText;
public class TickSpeed
@@ -20,11 +21,12 @@ public class TickSpeed
public static long time_bias = 0;
public static long time_warp_start_time = 0;
public static long time_warp_scheduled_ticks = 0;
public static PlayerEntity time_advancerer = null;
public static ServerPlayerEntity time_advancerer = null;
public static String tick_warp_callback = null;
public static ServerCommandSource tick_warp_sender = null;
public static int player_active_timeout = 0;
public static boolean process_entities = true;
public static boolean deepFreeze = false;
public static boolean is_paused = false;
public static boolean is_superHot = false;
@@ -43,18 +45,37 @@ public class TickSpeed
}
}
public static void reset()
{
tickrate = 20.0f;
mspt = 50.0f;
time_bias = 0;
time_warp_start_time = 0;
time_warp_scheduled_ticks = 0;
time_advancerer = null;
tick_warp_callback = null;
tick_warp_sender = null;
player_active_timeout = 0;
process_entities = true;
deepFreeze = false;
is_paused = false;
is_superHot = false;
notifyTickrateListeners("carpet");
}
public static void add_ticks_to_run_in_pause(int ticks)
{
player_active_timeout = PLAYER_GRACE+ticks;
}
public static BaseText tickrate_advance(PlayerEntity player, int advance, String callback, ServerCommandSource source)
public static BaseText tickrate_advance(ServerPlayerEntity player, int advance, String callback, ServerCommandSource source)
{
if (0 == advance)
{
tick_warp_callback = null;
tick_warp_sender = null;
if (source != tick_warp_sender) tick_warp_sender = null;
finish_time_warp();
tick_warp_sender = null;
return Messenger.c("gi Warp interrupted");
}
if (time_bias > 0)
@@ -157,8 +178,9 @@ public class TickSpeed
}
}
}
public static void tickrate(float rate)
//unused - mod compat reasons
public static void tickrate(float rate) {tickrate(rate, true);}
public static void tickrate(float rate, boolean update)
{
tickrate = rate;
long mspt = (long)(1000.0 / tickrate);
@@ -170,7 +192,7 @@ public class TickSpeed
TickSpeed.mspt = (float)mspt;
notifyTickrateListeners("carpet");
if (update) notifyTickrateListeners("carpet");
}
private static void tickrateChanged(String modId, float rate)
@@ -202,6 +224,7 @@ public class TickSpeed
}
}
}
ServerNetworkHandler.updateTickSpeedToConnectedPlayers();
}
public static BiConsumer<String, Float> addTickrateListener(String modId, BiConsumer<String, Float> tickrateListener)
@@ -30,6 +30,7 @@ public class LoggerRegistry
public static boolean __packets;
public static boolean __weather;
public static boolean __pathfinding;
public static boolean __explosions;
public static void initLoggers()
{
@@ -52,6 +53,7 @@ public class LoggerRegistry
registerLogger("packets", HUDLogger.stardardHUDLogger("packets", null, null));
registerLogger("counter",HUDLogger.stardardHUDLogger("counter","white", Arrays.stream(DyeColor.values()).map(Object::toString).toArray(String[]::new)));
registerLogger("mobcaps", HUDLogger.stardardHUDLogger("mobcaps", "dynamic",new String[]{"dynamic", "overworld", "nether","end"}));
registerLogger("explosions", HUDLogger.stardardLogger("explosions", "brief",new String[]{"brief", "full"}));
}
@@ -0,0 +1,125 @@
package carpet.logging.logHelpers;
import carpet.logging.LoggerRegistry;
import carpet.utils.Messenger;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.text.BaseText;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.explosion.Explosion;
import java.util.ArrayList;
import java.util.List;
import static carpet.utils.Messenger.c;
public class ExplosionLogHelper
{
private final boolean createFire;
private final Explosion.DestructionType blockDestructionType;
public final Vec3d pos;
public final Entity entity;
private final float power;
private boolean affectBlocks = false;
private Object2IntMap<EntityChangedStatusWithCount> impactedEntities = new Object2IntOpenHashMap<>();
private static long lastGametime = 0;
private static int explosionCountInCurretGT = 0;
public ExplosionLogHelper(Entity entity, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType blockDestructionType) {
this.entity = entity;
this.power = power;
this.pos = new Vec3d(x,y,z);
this.createFire = createFire;
this.blockDestructionType = blockDestructionType;
}
public void setAffectBlocks(boolean b)
{
affectBlocks = b;
}
public void onExplosionDone(long gametime)
{
List<BaseText> messages = new ArrayList<>();
if (!(lastGametime == gametime)){
explosionCountInCurretGT = 0;
lastGametime = gametime;
messages.add(c("wb tick : ", "d " + gametime));
}
explosionCountInCurretGT++;
LoggerRegistry.getLogger("explosions").log( (option) -> {
if ("brief".equals(option))
{
messages.add( c("d #" + explosionCountInCurretGT,"gb ->",
Messenger.dblt("l", pos.x, pos.y, pos.z), (affectBlocks)?"m (affects blocks)":"m (doesn't affect blocks)" ));
}
if ("full".equals(option))
{
messages.add( c("d #" + explosionCountInCurretGT,"gb ->", Messenger.dblt("l", pos.x, pos.y, pos.z) ));
messages.add(c("w affects blocks: ", "m " + this.affectBlocks));
messages.add(c("w creates fire: ", "m " + this.createFire));
messages.add(c("w power: ", "c " + this.power));
messages.add(c( "w destruction: ", "c " + this.blockDestructionType.name()));
if (impactedEntities.isEmpty())
{
messages.add(c("w affected entities: ", "m None"));
}
else
{
messages.add(c("w affected entities:"));
impactedEntities.forEach((k, v) ->
{
messages.add(c((k.pos.equals(pos))?"r - TNT":"w - ",
Messenger.dblt((k.pos.equals(pos))?"r":"y", k.pos.x, k.pos.y, k.pos.z), "w dV",
Messenger.dblt("d", k.accel.x, k.accel.y, k.accel.z),
"w "+Registry.ENTITY_TYPE.getId(k.type).getPath(), (v>1)?"l ("+v+")":""
));
});
}
}
return messages.toArray(new BaseText[0]);
});
}
public void onEntityImpacted(Entity entity, Vec3d accel)
{
EntityChangedStatusWithCount ent = new EntityChangedStatusWithCount(entity, accel);
impactedEntities.put(ent, impactedEntities.getOrDefault(ent, 0)+1);
}
public static class EntityChangedStatusWithCount
{
public final Vec3d pos;
public final EntityType type;
public final Vec3d accel;
public EntityChangedStatusWithCount(Entity e, Vec3d accel)
{
this.pos = e.getPos();
this.type = e.getType();
this.accel = accel;
}
@Override
public boolean equals(Object obj)
{
if (obj instanceof EntityChangedStatusWithCount)
{
EntityChangedStatusWithCount other = (EntityChangedStatusWithCount) obj;
return other.pos.equals(pos) && other.accel.equals(accel) && other.type.equals(type);
}
return super.equals(obj);
}
@Override
public int hashCode()
{
return pos.hashCode()+ type.hashCode()+accel.hashCode();
}
}
}
@@ -9,6 +9,8 @@ public class TNTLogHelper
{
public boolean initialized;
private double primedX, primedY, primedZ;
private static long lastGametime = 0;
private static int tntCount = 0;
private Vec3d primedAngle;
/**
* Runs when the TNT is primed. Expects the position and motion angle of the TNT.
@@ -24,8 +26,13 @@ public class TNTLogHelper
/**
* Runs when the TNT explodes. Expects the position of the TNT.
*/
public void onExploded(double x, double y, double z)
public void onExploded(double x, double y, double z, long gametime)
{
if (!(lastGametime == gametime)){
tntCount = 0;
lastGametime = gametime;
}
tntCount++;
LoggerRegistry.getLogger("tnt").log( (option) -> {
switch (option)
{
@@ -36,6 +43,9 @@ public class TNTLogHelper
"r E ",Messenger.dblt("r",x, y, z))};
case "full":
return new BaseText[]{Messenger.c(
"r #" + String.valueOf(tntCount),
"m @" + String.valueOf(gametime),
"g : ",
"l P ",Messenger.dblf("l",primedX,primedY,primedZ),
"w ",Messenger.dblf("l", primedAngle.x, primedAngle.y, primedAngle.z),
"r E ",Messenger.dblf("r",x, y, z))};
@@ -0,0 +1,36 @@
package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EntityContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(BlockItem.class)
public class BlockItem_creativeNoClipMixin
{
@Redirect(method = "canPlace", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/World;canPlace(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/entity/EntityContext;)Z"
))
private boolean canSpectatingPlace(World world, BlockState state, BlockPos pos, EntityContext context,
ItemPlacementContext contextOuter, BlockState stateOuter)
{
PlayerEntity player = contextOuter.getPlayer();
if (CarpetSettings.creativeNoClip && player != null && player.isCreative() && player.abilities.flying)
{
// copy from canPlace
VoxelShape voxelShape = state.getCollisionShape(world, pos, context);
return voxelShape.isEmpty() || world.intersectsEntities(player, voxelShape.offset(pos.getX(), pos.getY(), pos.getZ()));
}
return world.canPlace(state, pos, context);
}
}
@@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Chunk.class)
public abstract class ChunkMixin
{
// failed to mixin into interface. need to mixin in two places that uses it
@Inject(method = "markBlockForPostProcessing(Lnet/minecraft/util/math/BlockPos;)V",
at =@At("HEAD"), cancellable = true)
private void squashWarnings(BlockPos blockPos_1, CallbackInfo ci)
@@ -0,0 +1,19 @@
package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ChunkRegion;
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.callback.CallbackInfo;
@Mixin(ChunkRegion.class)
public class ChunkRegion_scarpetPlopMixin
{
@Inject(method = "markBlockForPostProcessing", at = @At("HEAD"))
private void markOrNot(BlockPos blockPos, CallbackInfo ci)
{
if (CarpetSettings.skipGenerationChecks) ci.cancel();
}
}
@@ -1,15 +1,23 @@
package carpet.mixins;
import carpet.fakes.ChunkTicketManagerInterface;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.server.world.ChunkTicket;
import net.minecraft.server.world.ChunkTicketManager;
import net.minecraft.util.SortedArraySet;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(ChunkTicketManager.class)
public interface ChunkTicketManager_scarpetMixin
public abstract class ChunkTicketManager_scarpetMixin implements ChunkTicketManagerInterface
{
@Accessor
Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition();
@Shadow @Final private Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> ticketsByPosition;
@Override
public Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition()
{
return ticketsByPosition;
}
}
@@ -0,0 +1,60 @@
package carpet.mixins;
import carpet.fakes.ChunkTicketManagerInterface;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.server.world.ChunkTicket;
import net.minecraft.server.world.ChunkTicketManager;
import net.minecraft.server.world.ChunkTicketType;
import net.minecraft.util.SortedArraySet;
import net.minecraft.util.Unit;
import net.minecraft.util.math.ChunkPos;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Iterator;
@Mixin(ChunkTicketManager.class)
public abstract class ChunkTicketManager_spawnChunksMixin implements ChunkTicketManagerInterface
{
@Shadow protected abstract SortedArraySet<ChunkTicket<?>> getTicketSet(long position);
@Shadow @Final private Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> ticketsByPosition;
@Shadow public abstract <T> void removeTicket(ChunkTicketType<T> type, ChunkPos pos, int radius, T argument);
@Shadow protected abstract void removeTicket(long pos, ChunkTicket<?> ticket);
@Shadow public abstract <T> void addTicket(ChunkTicketType<T> type, ChunkPos pos, int radius, T argument);
@Override
public void changeSpawnChunks(ChunkPos chunkPos, int distance)
{
long pos = chunkPos.toLong();
SortedArraySet<ChunkTicket<?>> set = ticketsByPosition.get(pos);
ChunkTicket existingTicket = null;
if (set != null)
{
Iterator<ChunkTicket<?>> iter = set.iterator();
while(iter.hasNext())
{
ChunkTicket ticket = iter.next();
if (ticket.getType() == ChunkTicketType.START)
{
existingTicket = ticket;
iter.remove();
}
}
set.add(existingTicket);
}
// the reason we are removing the ticket this way is that there are sideeffects of removal
if (existingTicket != null)
{
removeTicket(pos, existingTicket);
}
// set optionally new spawn ticket
if (distance > 0)
addTicket(ChunkTicketType.START, chunkPos, distance, Unit.INSTANCE);
}
}
@@ -0,0 +1,23 @@
package carpet.mixins;
import carpet.helpers.TickSpeed;
import net.minecraft.server.world.ChunkTicketManager;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ChunkTicketManager.class)
public class ChunkTicketManager_tickMixin
{
@Shadow private long age;
@Inject(method = "purge", at = @At("HEAD"), cancellable = true)
private void pauseTicketSystem(CallbackInfo ci)
{
// pausing expiry of tickets
// that will prevent also chunks from unloading, so require a deep frozen state
if (!TickSpeed.process_entities && TickSpeed.deepFreeze) age--;
}
}
@@ -0,0 +1,43 @@
package carpet.mixins;
import carpet.network.CarpetClient;
import carpet.network.ClientNetworkHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
import net.minecraft.text.Text;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin
{
@Shadow private MinecraftClient client;
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
private void onOnCustomPayload(CustomPayloadS2CPacket packet, CallbackInfo ci)
{
if (CarpetClient.CARPET_CHANNEL.equals(packet.getChannel()))
{
ClientNetworkHandler.handleData(packet.getData(), client.player);
ci.cancel();
}
}
@Inject(method = "onGameJoin", at = @At("RETURN"))
private void onGameJoined(GameJoinS2CPacket packet, CallbackInfo info)
{
CarpetClient.gameJoined(client.player);
}
@Inject(method = "onDisconnected", at = @At("HEAD"))
private void onCMDisconnected(Text reason, CallbackInfo ci)
{
CarpetClient.disconnect();
}
}
@@ -1,6 +1,7 @@
package carpet.mixins;
import carpet.CarpetServer;
import carpet.network.CarpetClient;
import net.minecraft.client.network.ClientPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -13,7 +14,10 @@ public class ClientPlayerEntity_clientCommandMixin
@Inject(method = "sendChatMessage", at = @At("HEAD"))
private void inspectMessage(String string, CallbackInfo ci)
{
if (CarpetServer.minecraft_server == null)
CarpetServer.settingsManager.inspectClientsideCommand(string);
if (CarpetServer.minecraft_server == null && !CarpetClient.isCarpet())
{
ClientPlayerEntity playerSource = (ClientPlayerEntity)(Object) this;
CarpetServer.settingsManager.inspectClientsideCommand(playerSource.getCommandSource(), string);
}
}
}
@@ -0,0 +1,27 @@
package carpet.mixins;
import carpet.fakes.CustomPayloadC2SPacketInterface;
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(CustomPayloadC2SPacket.class)
public class CustomPayloadC2SPacketMixin implements CustomPayloadC2SPacketInterface
{
@Shadow
private Identifier channel;
@Shadow
private PacketByteBuf data;
@Override
public Identifier getPacketChannel() {
return channel;
}
@Override
public PacketByteBuf getPacketData() {
return new PacketByteBuf(this.data.copy());
}
}
@@ -5,6 +5,7 @@ import carpet.script.EntityEventsGroup;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.damage.DamageTracker;
import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -12,8 +13,11 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static carpet.script.CarpetEventServer.Event.PLAYER_DEALS_DAMAGE;
import static carpet.script.CarpetEventServer.Event.PLAYER_TAKES_DAMAGE;
@Mixin(DamageTracker.class)
public class DamageTracker_scarpetEventMixin
public abstract class DamageTracker_scarpetEventMixin
{
@Shadow @Final private LivingEntity entity;
@@ -21,5 +25,13 @@ public class DamageTracker_scarpetEventMixin
private void onDamageTaken(DamageSource damageSource_1, float float_1, float float_2, CallbackInfo ci)
{
((EntityInterface)entity).getEventContainer().onEvent(EntityEventsGroup.EntityEventType.ON_DAMAGE, entity, float_2, damageSource_1);
if (entity instanceof ServerPlayerEntity && PLAYER_TAKES_DAMAGE.isNeeded())
{
PLAYER_TAKES_DAMAGE.onDamage(entity, float_2, damageSource_1);
}
if (damageSource_1.getAttacker() instanceof ServerPlayerEntity && PLAYER_DEALS_DAMAGE.isNeeded())
{
PLAYER_DEALS_DAMAGE.onDamage(entity, float_2, damageSource_1);
}
}
}
@@ -0,0 +1,19 @@
package carpet.mixins;
import carpet.network.CarpetClient;
import net.minecraft.client.render.debug.DebugRenderer;
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.callback.CallbackInfo;
@Mixin(DebugRenderer.class)
public class DebugRenderer_scarpetRenderMixin
{
@Inject(method = "reset", at = @At("HEAD"))
private void resetScarpetRenderes(CallbackInfo ci)
{
if (CarpetClient.shapes != null)
CarpetClient.shapes.reset();
}
}
@@ -14,6 +14,7 @@ public abstract class Entity_scarpetEventsMixin implements EntityInterface
{
@Shadow public boolean removed;
@Shadow protected int netherPortalTime;
private boolean permanentVehicle;
private final EntityEventsGroup events = new EntityEventsGroup();
@@ -36,6 +37,18 @@ public abstract class Entity_scarpetEventsMixin implements EntityInterface
permanentVehicle = permanent;
}
@Override
public int getPortalTimer()
{
return netherPortalTime;
}
@Override
public void setPortalTimer(int amount)
{
netherPortalTime = amount;
}
@Inject(method = "tick", at = @At("HEAD"))
private void onTickCall(CallbackInfo ci)
{
@@ -2,9 +2,13 @@ package carpet.mixins;
import carpet.helpers.OptimizedExplosion;
import carpet.CarpetSettings;
import carpet.logging.LoggerRegistry;
import carpet.logging.logHelpers.ExplosionLogHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.spongepowered.asm.mixin.Final;
@@ -24,13 +28,17 @@ public abstract class ExplosionMixin
@Final
private List<BlockPos> affectedBlocks;
@Shadow @Final private World world;
private ExplosionLogHelper eLogger;
@Inject(method = "collectBlocksAndDamageEntities", at = @At("HEAD"),
cancellable = true)
private void onExplosionA(CallbackInfo ci)
{
if (CarpetSettings.optimizedTNT)
{
OptimizedExplosion.doExplosionA((Explosion) (Object) this);
OptimizedExplosion.doExplosionA((Explosion) (Object) this, eLogger);
ci.cancel();
}
}
@@ -39,6 +47,11 @@ public abstract class ExplosionMixin
cancellable = true)
private void onExplosionB(boolean spawnParticles, CallbackInfo ci)
{
if (eLogger != null)
{
eLogger.setAffectBlocks( ! affectedBlocks.isEmpty());
eLogger.onExplosionDone(this.world.getTime());
}
if (CarpetSettings.explosionNoBlockDamage)
{
affectedBlocks.clear();
@@ -58,4 +71,24 @@ public abstract class ExplosionMixin
if (CarpetSettings.explosionNoBlockDamage) return Blocks.BEDROCK.getDefaultState();
return world.getBlockState(pos);
}
@Inject(method = "<init>(Lnet/minecraft/world/World;Lnet/minecraft/entity/Entity;DDDFZLnet/minecraft/world/explosion/Explosion$DestructionType;)V",
at = @At(value = "RETURN"))
private void onExplosionCreated(World world, Entity entity, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType blockDestructionType, CallbackInfo ci)
{
if (LoggerRegistry.__explosions && ! world.isClient)
{
eLogger = new ExplosionLogHelper(entity, x, y, z, power, createFire, blockDestructionType);
}
}
@Redirect(method = "collectBlocksAndDamageEntities",
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;setVelocity(Lnet/minecraft/util/math/Vec3d;)V"))
private void setVelocityAndUpdateLogging(Entity entity, Vec3d velocity)
{
if (eLogger != null) {
eLogger.onEntityImpacted(entity, velocity.subtract(entity.getVelocity()));
}
entity.setVelocity(velocity);
}
}
@@ -0,0 +1,20 @@
package carpet.mixins;
import carpet.fakes.HungerManagerInterface;
import net.minecraft.entity.player.HungerManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(HungerManager.class)
public class HungerManagerMixin_scarpetEventsMixin implements HungerManagerInterface {
@Shadow private float exhaustion;
public float getExhaustionCM(){
return exhaustion;
}
@Override
public void setExhaustionCM(float exhaust)
{
exhaustion = exhaust;
}
}
@@ -30,12 +30,12 @@ public abstract class ItemEntityMixin extends Entity implements ItemEntityInterf
}
@Override
public int getAge() {
public int getAgeCM() {
return this.age;
}
@Override
public int getPickupDelay() {
public int getPickupDelayCM() {
return this.pickupDelay;
}
@@ -91,8 +91,8 @@ public abstract class ItemEntityMixin extends Entity implements ItemEntityInterf
selfStack.increment(amount);
self.setStack(selfStack);
this.pickupDelay = Math.max(((ItemEntityInterface)other).getPickupDelay(), this.pickupDelay);
this.age = Math.min(((ItemEntityInterface)other).getAge(), this.age);
this.pickupDelay = Math.max(((ItemEntityInterface)other).getPickupDelayCM(), this.pickupDelay);
this.age = Math.min(((ItemEntityInterface)other).getAgeCM(), this.age);
otherStack.decrement(amount);
if (otherStack.isEmpty())
@@ -0,0 +1,50 @@
package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.World;
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.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LivingEntity.class)
public abstract class LivingEntity_creativeFlyMixin extends Entity
{
@Shadow public float flyingSpeed;
public LivingEntity_creativeFlyMixin(EntityType<?> type, World world)
{
super(type, world);
}
@ModifyConstant(method = "travel", constant = @Constant(floatValue = 0.91F), expect = 2)
private float drag(float original)
{
if (CarpetSettings.creativeFlyDrag != 0.09 && (Object)this instanceof PlayerEntity)
{
PlayerEntity self = (PlayerEntity)(Object)(this);
if (self.abilities.flying && ! onGround )
return (float)(1.0-CarpetSettings.creativeFlyDrag);
}
return original;
}
@Inject(method = "getMovementSpeed(F)F", at = @At("HEAD"), cancellable = true)
private void flyingAltSpeed(float slipperiness, CallbackInfoReturnable<Float> cir)
{
if (CarpetSettings.creativeFlySpeed != 1.0D && (Object)this instanceof PlayerEntity)
{
PlayerEntity self = (PlayerEntity)(Object)(this);
if (self.abilities.flying && !onGround)
cir.setReturnValue(flyingSpeed* (float)CarpetSettings.creativeFlySpeed);
}
}
}
@@ -1,21 +1,33 @@
package carpet.mixins;
import carpet.CarpetSettings;
import carpet.fakes.EntityInterface;
import carpet.fakes.LivingEntityInterface;
import carpet.script.EntityEventsGroup;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.World;
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.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LivingEntity.class)
public abstract class LivingEntity_scarpetEventsMixin extends Entity
public abstract class LivingEntity_scarpetEventsMixin extends Entity implements LivingEntityInterface
{
@Shadow protected abstract void jump();
@Shadow protected boolean jumping;
public LivingEntity_scarpetEventsMixin(EntityType<?> type, World world)
{
super(type, world);
@@ -26,4 +38,16 @@ public abstract class LivingEntity_scarpetEventsMixin extends Entity
{
((EntityInterface)this).getEventContainer().onEvent(EntityEventsGroup.EntityEventType.ON_DEATH, this, damageSource_1.name);
}
@Override
public void doJumpCM()
{
jump();
}
@Override
public boolean isJumpingCM()
{
return jumping;
}
}
@@ -1,7 +1,9 @@
package carpet.mixins;
import carpet.CarpetServer;
import carpet.network.CarpetClient;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -18,4 +20,10 @@ public class MinecraftClientMixin
//CM start game hook
CarpetServer.onGameStarted();
}
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V", at = @At("HEAD"))
private void onCloseGame(Screen screen, CallbackInfo ci)
{
CarpetClient.disconnect();
}
}
@@ -1,8 +1,10 @@
package carpet.mixins;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import com.google.gson.JsonElement;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldGenerationProgressListener;
import net.minecraft.world.level.LevelGeneratorType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -41,4 +43,11 @@ public abstract class MinecraftServer_coreMixin
{
CarpetServer.onServerClosed((MinecraftServer) (Object) this);
}
@Inject(method = "prepareStartRegion", at = @At("RETURN"))
private void afterSpawnCreated(WorldGenerationProgressListener worldGenerationProgressListener, CallbackInfo ci)
{
if (CarpetSettings.spawnChunksSize != 11)
CarpetSettings.ChangeSpawnChunksValidator.changeSpawnSize(CarpetSettings.spawnChunksSize);
}
}
@@ -120,7 +120,7 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
this.profiler.startTick();
this.profiler.push("tick");
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->true : this::shouldKeepTicking);
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->!hasRunningTasks() : this::shouldKeepTicking);
this.profiler.swap("nextTickWait");
this.field_19249 = true;
this.field_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference);
@@ -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);
}
}
@@ -5,6 +5,7 @@ import carpet.fakes.PistonBlockEntityInterface;
import com.google.common.collect.Lists;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.piston.PistonBehavior;
import net.minecraft.block.piston.PistonHandler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
@@ -62,6 +63,17 @@ public abstract class PistonBlock_movableTEMixin extends FacingBlock
return !(CarpetSettings.movableBlockEntities && isPushableBlockEntity(block));
}
}
@Redirect(method = "isMovable", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;getPistonBehavior()Lnet/minecraft/block/piston/PistonBehavior;"
))
private static PistonBehavior moveGrindstones(BlockState blockState)
{
if (CarpetSettings.movableBlockEntities && blockState.getBlock() == Blocks.GRINDSTONE) return PistonBehavior.NORMAL;
return blockState.getPistonBehavior();
}
@Inject(method = "move", at = @At(value = "INVOKE", shift = At.Shift.BEFORE,
target = "Ljava/util/List;size()I", ordinal = 4),locals = LocalCapture.CAPTURE_FAILHARD)
private void onMove(World world_1, BlockPos blockPos_1, Direction direction_1, boolean boolean_1,
@@ -23,7 +23,7 @@ public abstract class PlayerEntity_antiCheatDisabled
@Inject(method = "method_23668", at = @At("HEAD"), cancellable = true)
private void allowDeploys(CallbackInfoReturnable<Boolean> cir)
{
if (CarpetSettings.antiCheatDisabled && CarpetServer.minecraft_server.isDedicated())
if (CarpetSettings.antiCheatDisabled && CarpetServer.minecraft_server != null && CarpetServer.minecraft_server.isDedicated())
{
ItemStack itemStack_1 = getEquippedStack(EquipmentSlot.CHEST);
if (itemStack_1.getItem() == Items.ELYTRA && ElytraItem.isUsable(itemStack_1)) {
@@ -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);
}
}
@@ -9,6 +9,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static carpet.script.CarpetEventServer.Event.PLAYER_CONNECTS;
@Mixin(PlayerManager.class)
public class PlayerManager_coreMixin
{
@@ -16,5 +18,6 @@ public class PlayerManager_coreMixin
private void onPlayerConnected(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci)
{
CarpetServer.onPlayerLoggedIn(player);
PLAYER_CONNECTS.onPlayerEvent(player);
}
}
@@ -0,0 +1,21 @@
package carpet.mixins;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.world.dimension.DimensionType;
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.callback.CallbackInfoReturnable;
import static carpet.script.CarpetEventServer.Event.PLAYER_RESPAWNS;
@Mixin(PlayerManager.class)
public class PlayerManager_scarpetEventsMixin
{
@Inject(method = "respawnPlayer", at = @At("HEAD"))
private void onRespawn(ServerPlayerEntity player, DimensionType dimension, boolean alive, CallbackInfoReturnable<ServerPlayerEntity> cir)
{
PLAYER_RESPAWNS.onPlayerEvent(player);
}
}
@@ -1,8 +1,10 @@
package carpet.mixins;
import carpet.fakes.ServerChunkManagerInterface;
import carpet.utils.SpawnReporter;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.entity.EntityCategory;
import net.minecraft.server.world.ChunkTicketManager;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
@@ -24,12 +26,20 @@ import java.util.HashMap;
import java.util.HashSet;
@Mixin(ServerChunkManager.class)
public abstract class ServerChunkManagerMixin
public abstract class ServerChunkManagerMixin implements ServerChunkManagerInterface
{
@Shadow @Final private ServerWorld world;
@Shadow @Final private static int CHUNKS_ELIGIBLE_FOR_SPAWNING;
@Shadow @Final private ChunkTicketManager ticketManager;
@Override // shared between scarpet and spawnChunks setting
public ChunkTicketManager getCMTicketManager()
{
return ticketManager;
}
@Inject(
method = "tickChunks",
locals = LocalCapture.CAPTURE_FAILHARD,
@@ -1,13 +0,0 @@
package carpet.mixins;
import net.minecraft.server.world.ChunkTicketManager;
import net.minecraft.server.world.ServerChunkManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ServerChunkManager.class)
public interface ServerChunkManager_scarpetMixin
{
@Accessor
ChunkTicketManager getTicketManager();
}
@@ -0,0 +1,31 @@
package carpet.mixins;
import carpet.fakes.CustomPayloadC2SPacketInterface;
import carpet.network.CarpetClient;
import carpet.network.ServerNetworkHandler;
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayNetworkHandler.class)
public class ServerPlayNetworkHandlerMixin
{
@Shadow public ServerPlayerEntity player;
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
private void onCustomCarpetPayload(CustomPayloadC2SPacket packet, CallbackInfo ci)
{
Identifier channel = ((CustomPayloadC2SPacketInterface) packet).getPacketChannel();
if (CarpetClient.CARPET_CHANNEL.equals(channel))
{
ServerNetworkHandler.handleData(((CustomPayloadC2SPacketInterface) packet).getPacketData(), player);
ci.cancel();
}
}
}
@@ -13,6 +13,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static carpet.script.CarpetEventServer.Event.PLAYER_DISCONNECTS;
@Mixin(ServerPlayNetworkHandler.class)
public class ServerPlayNetworkHandler_coreMixin {
@Shadow
@@ -21,5 +23,6 @@ public class ServerPlayNetworkHandler_coreMixin {
@Inject(method = "onDisconnected", at = @At("HEAD"))
private void onPlayerDisconnect(Text reason, CallbackInfo ci) {
CarpetServer.onPlayerLoggedOut(this.player);
if (PLAYER_DISCONNECTS.isNeeded()) PLAYER_DISCONNECTS.onPlayerMessage(player, reason.asString());
}
}
@@ -31,6 +31,10 @@ public class ServerPlayNetworkHandler_tickMixin
}
}
// to skip reposition adjustment check
private static long lastMovedTick = 0L;
private static double lastMoved = 0.0D;
@Inject(method = "onPlayerMove", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/network/ServerPlayerEntity;isSleeping()Z",
@@ -38,10 +42,23 @@ public class ServerPlayNetworkHandler_tickMixin
))
private void checkMove(PlayerMoveC2SPacket p, CallbackInfo ci)
{
if (Math.abs(p.getX(player.getX()) - lastTickX) > 0.0001D
|| Math.abs(p.getY(player.getY()) - lastTickY) > 0.0001D
|| Math.abs(p.getY(player.getZ()) - lastTickZ) > 0.0001D)
double movedBy = player.getPos().squaredDistanceTo(lastTickX, lastTickY, lastTickZ);
if (movedBy == 0.0D) return;
// corrective tick
if (movedBy < 0.0009 && lastMoved > 0.0009 && Math.abs(player.getServer().getTicks()-lastMovedTick-20)<2)
{
//CarpetSettings.LOG.error("Corrective movement packet");
return;
}
if (movedBy > 0.0D)
{
//CarpetSettings.LOG.error(String.format(
// "moved by %.6f at %d",
// player.getPos().squaredDistanceTo(lastTickX, lastTickY, lastTickZ),
// player.getServer().getTicks()-lastMovedTick
//));
lastMoved = movedBy;
lastMovedTick = player.getServer().getTicks();
TickSpeed.reset_player_active_timeout();
}
}
@@ -3,20 +3,26 @@ package carpet.mixins;
import carpet.fakes.EntityInterface;
import carpet.script.EntityEventsGroup;
import com.mojang.authlib.GameProfile;
import net.minecraft.entity.Entity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.stat.Stat;
import net.minecraft.util.Hand;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
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.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import static carpet.script.CarpetEventServer.Event.PLAYER_CHANGES_DIMENSION;
import static carpet.script.CarpetEventServer.Event.PLAYER_DIES;
import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM;
import static carpet.script.CarpetEventServer.Event.STATISTICS;
@@ -30,6 +36,8 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
@Shadow protected abstract void consumeItem();
@Shadow public boolean notInAnyWorld;
@Redirect(method = "consumeItem", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/entity/player/PlayerEntity;consumeItem()V"
@@ -61,6 +69,10 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
private void onDeathEvent(DamageSource source, CallbackInfo ci)
{
((EntityInterface)this).getEventContainer().onEvent(EntityEventsGroup.EntityEventType.ON_DEATH, this, source.name);
if (PLAYER_DIES.isNeeded())
{
PLAYER_DIES.onPlayerEvent((ServerPlayerEntity) (Object)this);
}
}
@Redirect(method = "method_14218", at = @At(
@@ -72,4 +84,30 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
if (!((EntityInterface)serverPlayerEntity.getVehicle()).isPermanentVehicle()) // won't since that method makes sure its not null
serverPlayerEntity.setSneaking(sneaking);
}
private Vec3d previousLocation;
private DimensionType previousDimension;
@Inject(method = "changeDimension", at = @At("HEAD"))
private void logPreviousCoordinates(DimensionType newDimension, CallbackInfoReturnable<Entity> cir)
{
previousLocation = getPos();
previousDimension = dimension;
}
@Inject(method = "changeDimension", at = @At("RETURN"))
private void atChangeDimension(DimensionType newDimension, CallbackInfoReturnable<Entity> cir)
{
if (PLAYER_CHANGES_DIMENSION.isNeeded())
{
ServerPlayerEntity player = (ServerPlayerEntity) (Object)this;
Vec3d to = null;
if (!notInAnyWorld || previousDimension != DimensionType.THE_END || newDimension != DimensionType.OVERWORLD)
{
to = getPos();
}
PLAYER_CHANGES_DIMENSION.onDimensionChange(player, previousLocation, to, previousDimension, newDimension);
}
}
}
@@ -0,0 +1,17 @@
package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.server.world.ServerWorld;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
@Mixin(ServerWorld.class)
public class ServerWorld_spawnChunksMixin
{
@ModifyConstant(method = "setSpawnPos", constant = @Constant(intValue = 11), expect = 2)
private int pushLimit(int original)
{
return CarpetSettings.spawnChunksSize;
}
}
@@ -0,0 +1,22 @@
package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.structure.StructurePiece;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.Chunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(StructurePiece.class)
public class StructurePiece_scarpetPlopMixin
{
@Redirect(method = "addBlock", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/chunk/Chunk;markBlockForPostProcessing(Lnet/minecraft/util/math/BlockPos;)V"
))
private void markOrNot(Chunk chunk, BlockPos pos)
{
if (!CarpetSettings.skipGenerationChecks) chunk.markBlockForPostProcessing(pos);
}
}
@@ -72,7 +72,7 @@ public abstract class TntEntityMixin extends Entity implements TntEntityInterfac
private void onExplode(CallbackInfo ci)
{
if (LoggerRegistry.__tnt && logHelper != null)
logHelper.onExploded(getX(), getY(), getZ());
logHelper.onExploded(getX(), getY(), getZ(), this.world.getTime());
if (mergedTNT > 1)
for (int i = 0; i < mergedTNT - 1; i++)
@@ -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());
}
}
@@ -0,0 +1,23 @@
package carpet.mixins;
import carpet.CarpetSettings;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.world.dimension.Dimension;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = WorldRenderer.class, priority = 69420)
public class WorldRenderer_fogOffMixin
{
@Redirect(method = "render", require = 0, expect = 0, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/dimension/Dimension;isFogThick(II)Z"
))
private boolean isReallyThick(Dimension dimension, int x, int z)
{
if (CarpetSettings.fogOff) return false;
return dimension.isFogThick(x, z);
}
}
@@ -0,0 +1,32 @@
package carpet.mixins;
import carpet.network.CarpetClient;
import carpet.script.utils.ShapesRenderer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilderStorage;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.math.Matrix4f;
import net.minecraft.client.util.math.MatrixStack;
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.callback.CallbackInfo;
@Mixin(WorldRenderer.class)
public class WorldRenderer_scarpetRenderMixin
{
@Inject(method = "<init>", at = @At("RETURN"))
private void addRenderers(MinecraftClient client, BufferBuilderStorage bufferBuilders, CallbackInfo ci)
{
CarpetClient.shapes = new ShapesRenderer(client);
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/debug/DebugRenderer;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;DDD)V"))
private void renderScarpetThings(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci)
{
if (CarpetClient.shapes != null) CarpetClient.shapes.render(camera, tickDelta);
}
}
@@ -0,0 +1,54 @@
package carpet.network;
import carpet.CarpetServer;
import carpet.script.utils.ShapesRenderer;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.util.Identifier;
public class CarpetClient
{
public static final Object sync = new Object();
public static final int HI = 69;
public static final int HELLO = 420;
public static final int DATA = 1;
public static ShapesRenderer shapes = null;
private static ClientPlayerEntity clientPlayer = null;
private static boolean isServerCarpet = false;
public static String serverCarpetVersion;
public static final Identifier CARPET_CHANNEL = new Identifier("carpet:hello");
public static void gameJoined(ClientPlayerEntity player)
{
synchronized (sync)
{
clientPlayer = player;
// client didn't say hi back yet
if (isServerCarpet)
ClientNetworkHandler.respondHello();
}
}
public static void disconnect()
{
isServerCarpet = false;
clientPlayer = null;
CarpetServer.disconnect();
}
public static void setCarpet()
{
isServerCarpet = true;
}
public static ClientPlayerEntity getPlayer()
{
return clientPlayer;
}
public static boolean isCarpet()
{
return isServerCarpet;
}
}
@@ -0,0 +1,102 @@
package carpet.network;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.helpers.TickSpeed;
import carpet.settings.ParsedRule;
import io.netty.buffer.Unpooled;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.nbt.AbstractNumberTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
import net.minecraft.util.PacketByteBuf;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class ClientNetworkHandler
{
private static Map<String, BiConsumer<ClientPlayerEntity, Tag>> dataHandlers = new HashMap<String, BiConsumer<ClientPlayerEntity, Tag>>(){{
put("Rules", (p, t) -> {
CompoundTag ruleset = (CompoundTag)t;
for (String ruleName: ruleset.getKeys())
{
ParsedRule<?> rule = CarpetServer.settingsManager.getRule(ruleName);
if (rule == null)
CarpetSettings.LOG.error("Received unknown rule: "+ruleName);
else
{
CompoundTag ruleNBT = (CompoundTag) ruleset.get(ruleName);
String value = ruleNBT.getString("Value");
rule.set(null, value);
}
}
});
put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false));
put("scShape", (p, t) -> { // deprecated
if (CarpetClient.shapes != null)
CarpetClient.shapes.addShape((CompoundTag)t);
});
put("scShapes", (p, t) -> {
if (CarpetClient.shapes != null)
CarpetClient.shapes.addShapes((ListTag) t);
});
}};
public static void handleData(PacketByteBuf data, ClientPlayerEntity player)
{
if (data != null)
{
int id = data.readVarInt();
if (id == CarpetClient.HI)
onHi(data);
if (id == CarpetClient.DATA)
onSyncData(data, player);
}
}
private static void onHi(PacketByteBuf data)
{
synchronized (CarpetClient.sync)
{
CarpetClient.setCarpet();
CarpetClient.serverCarpetVersion = data.readString(64);
if (CarpetSettings.carpetVersion.equals(CarpetClient.serverCarpetVersion))
{
CarpetSettings.LOG.info("Joined carpet server with matching carpet version");
}
else
{
CarpetSettings.LOG.warn("Joined carpet server with another carpet version: "+CarpetClient.serverCarpetVersion);
}
if (CarpetClient.getPlayer() != null)
respondHello();
}
}
public static void respondHello()
{
CarpetClient.getPlayer().networkHandler.sendPacket(new CustomPayloadC2SPacket(
CarpetClient.CARPET_CHANNEL,
(new PacketByteBuf(Unpooled.buffer())).writeVarInt(CarpetClient.HELLO).writeString(CarpetSettings.carpetVersion)
));
}
private static void onSyncData(PacketByteBuf data, ClientPlayerEntity player)
{
CompoundTag compound = data.readCompoundTag();
if (compound == null) return;
for (String key: compound.getKeys())
{
if (dataHandlers.containsKey(key))
dataHandlers.get(key).accept(player, compound.get(key));
else
CarpetSettings.LOG.error("Unknown carpet data: "+key);
}
}
}
@@ -0,0 +1,178 @@
package carpet.network;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.helpers.TickSpeed;
import carpet.settings.ParsedRule;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.PacketByteBuf;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class ServerNetworkHandler
{
private static Map<ServerPlayerEntity, String> remoteCarpetPlayers = new HashMap<>();
private static Set<ServerPlayerEntity> validCarpetPlayers = new HashSet<>();
public static void handleData(PacketByteBuf data, ServerPlayerEntity player)
{
if (data != null)
{
int id = data.readVarInt();
if (id == CarpetClient.HELLO)
onHello(player, data);
}
}
public static void onPlayerJoin(ServerPlayerEntity playerEntity)
{
if (!playerEntity.networkHandler.connection.isLocal())
{
playerEntity.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
(new PacketByteBuf(Unpooled.buffer())).writeVarInt(CarpetClient.HI).writeString(CarpetSettings.carpetVersion)
));
}
else
{
validCarpetPlayers.add(playerEntity);
}
}
public static void onHello(ServerPlayerEntity playerEntity, PacketByteBuf packetData)
{
validCarpetPlayers.add(playerEntity);
String clientVersion = packetData.readString(64);
remoteCarpetPlayers.put(playerEntity, clientVersion);
if (clientVersion.equals(CarpetSettings.carpetVersion))
CarpetSettings.LOG.info("Player "+playerEntity.getName().getString()+" joined with a matching carpet client");
else
CarpetSettings.LOG.warn("Player "+playerEntity.getName().getString()+" joined with another carpet version: "+clientVersion);
DataBuilder data = DataBuilder.create().withTickRate();
CarpetServer.settingsManager.getRules().forEach(data::withRule);
playerEntity.networkHandler.sendPacket(new CustomPayloadS2CPacket(CarpetClient.CARPET_CHANNEL, data.build() ));
}
public static void updateRuleWithConnectedClients(ParsedRule<?> rule)
{
if (CarpetSettings.superSecretSetting) return;
for (ServerPlayerEntity player : remoteCarpetPlayers.keySet())
{
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
DataBuilder.create().withRule(rule).build()
));
}
}
public static void updateTickSpeedToConnectedPlayers()
{
if (CarpetSettings.superSecretSetting) return;
for (ServerPlayerEntity player : remoteCarpetPlayers.keySet())
{
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
DataBuilder.create().withTickRate().build()
));
}
}
public static void broadcastCustomCommand(String command, Tag data)
{
if (CarpetSettings.superSecretSetting) return;
for (ServerPlayerEntity player : validCarpetPlayers)
{
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
DataBuilder.create().withCustomNbt(command, data).build()
));
}
}
public static void sendCustomCommand(ServerPlayerEntity player, String command, Tag data)
{
if (isValidCarpetPlayer(player))
{
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
DataBuilder.create().withCustomNbt(command, data).build()
));
}
}
public static void onPlayerLoggedOut(ServerPlayerEntity player)
{
validCarpetPlayers.remove(player);
if (!player.networkHandler.connection.isLocal())
remoteCarpetPlayers.remove(player);
}
public static void close()
{
remoteCarpetPlayers.clear();
validCarpetPlayers.clear();
}
public static boolean isValidCarpetPlayer(ServerPlayerEntity player)
{
if (CarpetSettings.superSecretSetting) return false;
return validCarpetPlayers.contains(player);
}
private static class DataBuilder
{
private CompoundTag tag;
private static DataBuilder create()
{
return new DataBuilder();
}
private DataBuilder()
{
tag = new CompoundTag();
}
private DataBuilder withTickRate()
{
tag.putFloat("TickRate", TickSpeed.tickrate);
return this;
}
private DataBuilder withRule(ParsedRule<?> rule)
{
CompoundTag rules = (CompoundTag) tag.get("Rules");
if (rules == null)
{
rules = new CompoundTag();
tag.put("Rules", rules);
}
CompoundTag ruleNBT = new CompoundTag();
ruleNBT.putString("Value", rule.getAsString());
rules.put(rule.name, ruleNBT);
return this;
}
public DataBuilder withCustomNbt(String key, Tag value)
{
tag.put(key, value);
return this;
}
private PacketByteBuf build()
{
PacketByteBuf packetBuf = new PacketByteBuf(Unpooled.buffer());
packetBuf.writeVarInt(CarpetClient.DATA);
packetBuf.writeCompoundTag(tag);
return packetBuf;
}
}
}
@@ -12,6 +12,8 @@ import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.utils.Messenger;
import net.minecraft.block.BlockState;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
@@ -50,6 +52,7 @@ import java.util.stream.Collectors;
public class CarpetEventServer
{
public final List<ScheduledCall> scheduledCalls = new LinkedList<>();
public final MinecraftServer server;
public static class Callback
{
@@ -125,7 +128,13 @@ public class CarpetEventServer
List<LazyValue> argv = argumentSupplier.get(); // empty for onTickDone
ServerCommandSource source = cmdSourceSupplier.get();
assert argv.size() == reqArgs;
callList.removeIf(call -> !CarpetServer.scriptServer.runas(source, call.host, call.function, argv)); // this actually does the calls
List<Callback> fails = new ArrayList<>();
for (Callback call: callList)
{
if (!CarpetServer.scriptServer.runas(source, call.host, call.function, argv))
fails.add(call);
}
for (Callback call : fails) callList.remove(call);
}
}
public boolean addEventCall(ServerCommandSource source, String hostName, String funName, Function<ScriptHost, Boolean> verifier)
@@ -479,6 +488,90 @@ public class CarpetEventServer
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_TAKES_DAMAGE("player_takes_damage", 4, false)
{
@Override
public void onDamage(Entity target, float amount, DamageSource source)
{
handler.call( () ->
{
return Arrays.asList(
((c, t) -> new EntityValue(target)),
((c, t) -> new NumericValue(amount)),
((c, t) ->new StringValue(source.getName())),
((c, t) -> source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker()))
);
}, target::getCommandSource);
}
},
PLAYER_DEALS_DAMAGE("player_deals_damage", 3, false)
{
@Override
public void onDamage(Entity target, float amount, DamageSource source)
{
handler.call( () ->
{
return Arrays.asList(
((c, t) -> new EntityValue(source.getAttacker())),
((c, t) -> new NumericValue(amount)),
((c, t) -> new EntityValue(target))
);
}, () -> source.getAttacker().getCommandSource());
}
},
PLAYER_DIES("player_dies", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_RESPAWNS("player_respawns", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_CHANGES_DIMENSION("player_changes_dimension", 5, false)
{
@Override
public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, DimensionType fromDim, DimensionType dimTo)
{
// eligibility already checked in mixin
Value fromValue = ListValue.fromTriple(from.x, from.y, from.z);
Value toValue = (to == null)?Value.NULL:ListValue.fromTriple(to.x, to.y, to.z);
Value fromDimStr = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION_TYPE.getId(fromDim)));
Value toDimStr = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION_TYPE.getId(dimTo)));
handler.call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> fromValue),
((c, t) -> fromDimStr),
((c, t) -> toValue),
((c, t) -> toDimStr)
), player::getCommandSource);
}
},
PLAYER_CONNECTS("player_connects", 1, false) {
@Override
public void onPlayerEvent(ServerPlayerEntity player)
{
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
},
PLAYER_DISCONNECTS("player_disconnects", 2, false) {
@Override
public void onPlayerMessage(ServerPlayerEntity player, String message)
{
handler.call( () -> Arrays.asList(
((c, t) -> new EntityValue(player)),
((c, t) -> new StringValue(message))
), player::getCommandSource);
}
},
STATISTICS("statistic", 4, false)
{
private <T> Identifier getStatId(Stat<T> stat)
@@ -543,6 +636,7 @@ public class CarpetEventServer
public void onTick() { }
public void onChunkGenerated(ServerWorld world, Chunk chunk) { }
public void onPlayerEvent(ServerPlayerEntity player) { }
public void onPlayerMessage(ServerPlayerEntity player, String message) { }
public void onPlayerStatistic(ServerPlayerEntity player, Stat<?> stat, int amount) { }
public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { }
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { }
@@ -551,13 +645,18 @@ public class CarpetEventServer
public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) { }
public void onBlockPlaced(ServerPlayerEntity player, BlockPos pos, Hand enumhand, ItemStack itemstack) { }
public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { }
public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, DimensionType fromDim, DimensionType dimTo) {}
public void onDamage(Entity target, float amount, DamageSource source) { }
public void onWorldEvent(ServerWorld world, BlockPos pos) { }
public void onWorldEventFlag(ServerWorld world, BlockPos pos, int flag) { }
}
public CarpetEventServer()
public CarpetEventServer(MinecraftServer server)
{
this.server = server;
for (Event e: Event.values())
e.handler.callList.clear();
}
File diff suppressed because it is too large. Load diff
@@ -96,7 +96,7 @@ public class CarpetScriptHost extends ScriptHost
public void addUserDefinedFunction(Context ctx, Module module, String funName, FunctionValue function)
{
super.addUserDefinedFunction(ctx, module, funName, function);
// mcarpet
if (ctx.host.main != module) return; // not dealing with automatic imports / exports /configs / apps from imports
if (funName.startsWith("__")) // potential fishy activity
{
if (funName.startsWith("__on_")) // here we can make a determination if we want to only accept events from main module.
@@ -323,7 +323,7 @@ public class CarpetScriptHost extends ScriptHost
{
try
{
callUDF(BlockPos.ORIGIN, CarpetServer.minecraft_server.getCommandSource(), closing, Collections.emptyList());
callUDF(BlockPos.ORIGIN, scriptServer.server.getCommandSource(), closing, Collections.emptyList());
}
catch (InvalidCallbackException ignored)
{
@@ -334,8 +334,8 @@ public class CarpetScriptHost extends ScriptHost
FunctionValue userClosing = value.getFunction("__on_close");
if (userClosing != null)
{
ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key);
ServerCommandSource source = (player != null)?player.getCommandSource():CarpetServer.minecraft_server.getCommandSource();
ServerPlayerEntity player = scriptServer.server.getPlayerManager().getPlayer(key);
ServerCommandSource source = (player != null)?player.getCommandSource():scriptServer.server.getCommandSource();
try
{
((CarpetScriptHost) value).callUDF(BlockPos.ORIGIN, source, userClosing, Collections.emptyList());
@@ -347,7 +347,7 @@ public class CarpetScriptHost extends ScriptHost
});
String markerName = CarpetExpression.MARKER_STRING+"_"+((getName()==null)?"":getName());
for (ServerWorld world : CarpetServer.minecraft_server.getWorlds())
for (ServerWorld world : scriptServer.server.getWorlds())
{
for (Entity e : world.getEntities(EntityType.ARMOR_STAND, (as) -> as.getScoreboardTags().contains(markerName)))
{
@@ -361,32 +361,31 @@ public class CarpetScriptHost extends ScriptHost
private void dumpState()
{
main.saveData(null, globalState);
Module.saveData(main, null, globalState, false);
}
private Tag loadState()
{
return main.getData(null);
return Module.getData(main, null, false);
}
public Tag getGlobalState(String file)
public Tag readFileTag(String file, boolean isShared)
{
if (getName() == null ) return null;
if (getName() == null && !isShared) return null;
if (file != null)
return main.getData(file);
return Module.getData(main, file, isShared);
if (parent == null)
return globalState;
return ((CarpetScriptHost)parent).globalState;
}
public void setGlobalState(Tag tag, String file)
public boolean writeTagFile(Tag tag, String file, boolean isShared)
{
if (getName() == null ) return;
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
if (file!= null)
{
main.saveData(file, tag);
return;
return Module.saveData(main, file, tag, isShared);
}
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
@@ -396,8 +395,28 @@ public class CarpetScriptHost extends ScriptHost
responsibleHost.dumpState();
responsibleHost.saveTimeout = 200;
}
return true;
}
public boolean removeResourceFile(String resource, boolean isShared, String type)
{
if (getName() == null && !isShared) return false; //
return Module.dropExistingFile(main, resource, type.equals("nbt")?"nbt":"txt", isShared);
}
public boolean appendLogFile(String resource, boolean isShared, String type, List<String> data)
{
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
return Module.appendToTextFile(main, resource, type, isShared, data);
}
public List<String> readTextResource(String resource, boolean isShared)
{
if (getName() == null && !isShared) return null; //
return Module.listFile(main, resource, "txt", isShared);
}
public void tick()
{
if (this.saveTimeout > 0)
@@ -491,4 +510,9 @@ public class CarpetScriptHost extends ScriptHost
{
handleErrorWithStack(message, new CarpetExpressionException(exc.getMessage(), exc.stack));
}
public CarpetScriptServer getScriptServer()
{
return scriptServer;
}
}
@@ -15,6 +15,7 @@ import carpet.utils.Messenger;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.CommandNode;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.math.BlockPos;
@@ -34,6 +35,7 @@ import static net.minecraft.server.command.CommandManager.literal;
public class CarpetScriptServer
{
//make static for now, but will change that later:
public final MinecraftServer server;
public final CarpetScriptHost globalHost;
public final Map<String, CarpetScriptHost> modules;
public long tickStart;
@@ -47,16 +49,18 @@ public class CarpetScriptServer
add(new BundledModule("event_test", false));
add(new BundledModule("stats_test", false));
add(new BundledModule("math", true));
add(new BundledModule("chunk_display", false));
}};
public CarpetScriptServer()
public CarpetScriptServer(MinecraftServer server)
{
this.server = server;
ScriptHost.systemGlobals.clear();
events = new CarpetEventServer();
events = new CarpetEventServer(server);
modules = new HashMap<>();
tickStart = 0L;
stopAll = false;
holyMoly = CarpetServer.minecraft_server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet());
holyMoly = server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet());
globalHost = CarpetScriptHost.create(this, null, false, null);
}
@@ -64,10 +68,10 @@ public class CarpetScriptServer
{
if (CarpetSettings.scriptsAutoload)
{
Messenger.m(CarpetServer.minecraft_server.getCommandSource(), "Auto-loading world scarpet apps");
Messenger.m(server.getCommandSource(), "Auto-loading world scarpet apps");
for (String moduleName: listAvailableModules(false))
{
addScriptHost(CarpetServer.minecraft_server.getCommandSource(), moduleName, true, true);
addScriptHost(server.getCommandSource(), moduleName, true, true);
}
}
@@ -75,8 +79,8 @@ public class CarpetScriptServer
public Module getModule(String name, boolean allowLibraries)
{
File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts");
File folder = server.getLevelStorage().resolveFile(
server.getLevelName(), "scripts");
File[] listOfFiles = folder.listFiles();
if (listOfFiles != null)
for (File script : listOfFiles)
@@ -110,8 +114,8 @@ public class CarpetScriptServer
if (!mi.isLibrary()) moduleNames.add(mi.getName());
}
}
File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts");
File folder = server.getLevelStorage().resolveFile(
server.getLevelName(), "scripts");
File[] listOfFiles = folder.listFiles();
if (listOfFiles == null)
return moduleNames;
@@ -223,7 +227,7 @@ public class CarpetScriptServer
})));
}
Messenger.m(source, "gi "+hostName+" app loaded with /"+hostName+" command");
CarpetServer.minecraft_server.getCommandManager().getDispatcher().register(command);
server.getCommandManager().getDispatcher().register(command);
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
}
+33 -37
View File
@@ -11,6 +11,7 @@ import carpet.script.Fluff.QuadFunction;
import carpet.script.Fluff.QuinnFunction;
import carpet.script.Fluff.SexFunction;
import carpet.script.Fluff.TriFunction;
import carpet.script.argument.FunctionArgument;
import carpet.script.bundled.Module;
import carpet.script.exception.BreakStatement;
import carpet.script.exception.ContinueStatement;
@@ -417,6 +418,12 @@ public class Expression
return result;
}
public void alias(String copy, String original)
{
functions.put(copy, functions.get(original));
}
private void UserDefinedFunctionsAndControlFlow() // public just to get the javadoc right
{
// artificial construct to handle user defined functions and function definitions
@@ -439,19 +446,9 @@ public class Expression
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
if (t != Context.SIGNATURE) // just call the function
{
Value functionValue = lv.get(0).evalValue(c);
if (!(functionValue instanceof FunctionValue))
{
String name = functionValue.getString();
functionValue = c.host.getAssertFunction(module, name);
}
List<LazyValue> lvargs = new ArrayList<>(lv.size()-1);
for (int i=1; i< lv.size(); i++)
{
lvargs.add(lv.get(i));
}
FunctionValue fun = (FunctionValue)functionValue;
Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), lvargs).evalValue(c);
FunctionArgument functionArgument = FunctionArgument.findIn(c, module, lv, 0, false, true);
FunctionValue fun = functionArgument.function;
Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), functionArgument.args).evalValue(c);
return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?)
}
// gimme signature
@@ -657,9 +654,18 @@ public class Expression
throw new InternalExpressionException("Failed to resolve left hand side of the += operation");
}
Value key = ((LContainerValue) v1).getAddress();
Value res = cvi.get(key).add(v2);
cvi.put(key, res);
return (cc, tt) -> res;
Value value = cvi.get(key);
if (value instanceof ListValue || value instanceof MapValue)
{
((AbstractListValue) value).append(v2);
return (cc, tt) -> value;
}
else
{
Value res = value.add(v2);
cvi.put(key, res);
return (cc, tt) -> res;
}
}
v1.assertAssignable();
String varname = v1.getVariable();
@@ -1114,7 +1120,7 @@ public class Expression
{
Value rval= lv.get(0).evalValue(c);
if (!(rval instanceof AbstractListValue))
throw new InternalExpressionException("Second argument of 'for' function should be a list or iterator");
throw new InternalExpressionException("First argument of 'for' function should be a list or iterator");
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
LazyValue expr = lv.get(1);
//scoping
@@ -1558,7 +1564,7 @@ public class Expression
if (lv.size() == 1)
return new StringValue(format);
int argIndex = 1;
if (lv.get(1) instanceof ListValue)
if (lv.get(1) instanceof ListValue && lv.size() == 2)
{
lv = ((ListValue) lv.get(1)).getItems();
argIndex = 0;
@@ -1861,26 +1867,10 @@ public class Expression
addLazyFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) -> {
if (lv.size() == 0)
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
Value funcDesc = lv.get(0).evalValue(c);
if (!(funcDesc instanceof FunctionValue))
{
String name = funcDesc.getString();
funcDesc = c.host.getAssertFunction(module, name);
}
FunctionValue fun = (FunctionValue)funcDesc;
int extraargs = lv.size() - fun.getArguments().size();
if (extraargs != 1 && extraargs != 2)
{
throw new InternalExpressionException("Function takes "+fun.getArguments().size()+" arguments.");
}
List<LazyValue> lvargs = new ArrayList<>();
for (int i=0; i< fun.getArguments().size(); i++)
{
lvargs.add(lv.get(i+1));
}
FunctionArgument functionArgument = FunctionArgument.findIn(c, module, lv, 0, true, false);
Value queue = Value.NULL;
if (extraargs == 2) queue = lv.get(lv.size()-1).evalValue(c);
ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c, lvargs);
if (lv.size() > functionArgument.offset) queue = lv.get(functionArgument.offset).evalValue(c);
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.args);
Thread.yield();
return (cc, tt) -> thread;
});
@@ -1906,6 +1896,12 @@ public class Expression
return ((ThreadValue) v).join();
});
addLazyFunction("task_dock", 1, (c, t, lv) -> {
// pass through placeholder
// implmenetation should dock the task on the main thread.
return lv.get(0);
});
addUnaryFunction("task_completed", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'task_completed' could only be used with a task value");
+8 -3
View File
@@ -173,9 +173,9 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
}
else if (nextChar == 't')
{
throw new ExpressionException(context, this.expression, token,
"Tab character is not supported");
//token.append('\t');
//throw new ExpressionException(context, this.expression, token,
// "Tab character is not supported");
token.append('\t');
}
else if (nextChar == 'r')
{
@@ -201,6 +201,11 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
{
token.append(input.charAt(pos++));
linepos++;
if (ch=='\n')
{
lineno++;
linepos = 0;
}
if (pos == input.length() && expression != null && context != null)
throw new ExpressionException(context, this.expression, token, "Program truncated");
}
@@ -0,0 +1,10 @@
package carpet.script.argument;
public abstract class Argument
{
public int offset;
protected Argument(int offset)
{
this.offset = offset;
}
}
@@ -0,0 +1,144 @@
package carpet.script.argument;
import carpet.script.CarpetContext;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BlockValue;
import carpet.script.value.ListValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import net.minecraft.util.math.BlockPos;
import java.util.List;
public class BlockArgument extends Argument
{
public final BlockValue block;
private BlockArgument(BlockValue b, int o)
{
super(o);
block = b;
}
public static BlockArgument findIn(CarpetContext c, List<LazyValue> params, int offset)
{
return findIn(c, params,offset, false, false);
}
public static BlockArgument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString)
{
return findIn(c, params,offset, acceptString, false);
}
public static BlockArgument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString, boolean optional)
{
try
{
Value v1 = params.get(0 + offset).evalValue(c);
//add conditional from string name
if (optional && v1 instanceof NullValue)
{
return new BlockArgument(null, 1+offset);
}
if (acceptString && v1 instanceof StringValue)
{
return new BlockArgument(BlockValue.fromString(v1.getString()), 1+offset);
}
if (v1 instanceof BlockValue)
{
return new BlockArgument(((BlockValue) v1), 1+offset);
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
return new BlockArgument(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
1+offset);
}
int xpos = (int) NumericValue.asNumber(v1).getLong();
int ypos = (int) NumericValue.asNumber( params.get(1 + offset).evalValue(c)).getLong();
int zpos = (int) NumericValue.asNumber( params.get(2 + offset).evalValue(c)).getLong();
return new BlockArgument(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
3+offset
);
}
catch (IndexOutOfBoundsException e)
{
throw handleError(optional, acceptString);
}
}
public static BlockArgument findInValues(CarpetContext c, List<Value> params, int offset, boolean acceptString, boolean optional)
{
try
{
Value v1 = params.get(0 + offset);
//add conditional from string name
if (optional && v1 instanceof NullValue)
{
return new BlockArgument(null, 1+offset);
}
if (acceptString && v1 instanceof StringValue)
{
return new BlockArgument(BlockValue.fromString(v1.getString()), 1+offset);
}
if (v1 instanceof BlockValue)
{
return new BlockArgument(((BlockValue) v1), 1+offset);
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
return new BlockArgument(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
1+offset);
}
int xpos = (int) NumericValue.asNumber(v1).getLong();
int ypos = (int) NumericValue.asNumber( params.get(1 + offset)).getLong();
int zpos = (int) NumericValue.asNumber( params.get(2 + offset)).getLong();
return new BlockArgument(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
3+offset
);
}
catch (IndexOutOfBoundsException e)
{
throw handleError(optional, acceptString);
}
}
private static InternalExpressionException handleError(boolean optional, boolean acceptString)
{
String message = "Block-type argument should be defined either by three coordinates (a triple or by three arguments), or a block value";
if (acceptString)
message+=", or a string with block description";
if (optional)
message+=", or null";
return new InternalExpressionException(message);
}
}
@@ -0,0 +1,60 @@
package carpet.script.argument;
import carpet.script.CarpetContext;
import carpet.script.Context;
import carpet.script.LazyValue;
import carpet.script.bundled.Module;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.FunctionValue;
import carpet.script.value.Value;
import java.util.ArrayList;
import java.util.List;
public class FunctionArgument extends Argument
{
public FunctionValue function;
public List<LazyValue> args;
private FunctionArgument(FunctionValue function, int offset, List<LazyValue> args)
{
super(offset);
this.function = function;
this.args = args;
}
public static FunctionArgument findIn(Context c, Module module, List<LazyValue> params, int offset, boolean prematureEvaluation, boolean strict)
{
Value functionValue = params.get(offset).evalValue(c);
if (!(functionValue instanceof FunctionValue))
{
String name = functionValue.getString();
functionValue = c.host.getAssertFunction(module, name);
}
FunctionValue fun = (FunctionValue)functionValue;
int argsize = fun.getArguments().size();
int extraargs = params.size() - argsize - offset - 1;
if (extraargs < 0 || (extraargs > 0 && strict))
throw new InternalExpressionException("Function "+fun.getPrettyString()+" requires "+fun.getArguments().size()+" arguments");
List<LazyValue> lvargs = new ArrayList<>();
if (prematureEvaluation)
{
for (int i = 0; i < argsize; i++)
{
Value arg = params.get(offset + 1 + i).evalValue(c);
lvargs.add((cc, tt) -> arg);
}
}
else
{
for (int i = 0; i < argsize; i++) lvargs.add(params.get(offset + 1 + i));
}
return new FunctionArgument(fun, offset+1+argsize, lvargs);
}
public List<Value> resolveArgs(Context context, Integer type)
{
List<Value> res = new ArrayList<>(args.size());
for (LazyValue arg : args) res.add(arg.evalValue(context, type));
return res;
}
}
@@ -0,0 +1,140 @@
package carpet.script.argument;
import carpet.script.CarpetContext;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BlockValue;
import carpet.script.value.ListValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
import net.minecraft.util.math.Vec3d;
import java.util.List;
public class Vector3Argument extends Argument
{
public Vec3d vec;
public final double yaw;
public final double pitch;
public boolean fromBlock;
private Vector3Argument(Vec3d v, int o)
{
super(o);
this.vec = v;
this.yaw = 0.0D;
this.pitch = 0.0D;
}
private Vector3Argument(Vec3d v, int o, double y, double p)
{
super(o);
this.vec = v;
this.yaw = y;
this.pitch = p;
}
private Vector3Argument fromBlock()
{
fromBlock = true;
return this;
}
public static Vector3Argument findIn(CarpetContext c, List<LazyValue> params, int offset)
{
return findIn(c,params, offset, false);
}
public static Vector3Argument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean optionalDirection)
{
try
{
Value v1 = params.get(0 + offset).evalValue(c);
if (v1 instanceof BlockValue)
{
return (new Vector3Argument(new Vec3d(((BlockValue) v1).getPos()).add(0.5,0.5,0.5), 1+offset)).fromBlock();
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
Vec3d pos = new Vec3d(
NumericValue.asNumber(args.get(0)).getDouble(),
NumericValue.asNumber(args.get(1)).getDouble(),
NumericValue.asNumber(args.get(2)).getDouble());
double yaw = 0.0D;
double pitch = 0.0D;
if (args.size()>3 && optionalDirection)
{
yaw = NumericValue.asNumber(args.get(3)).getDouble();
pitch = NumericValue.asNumber(args.get(4)).getDouble();
}
return new Vector3Argument(pos,offset+1, yaw, pitch);
}
Vec3d pos = new Vec3d(
NumericValue.asNumber(v1).getDouble(),
NumericValue.asNumber(params.get(1 + offset).evalValue(c)).getDouble(),
NumericValue.asNumber(params.get(2 + offset).evalValue(c)).getDouble());
double yaw = 0.0D;
double pitch = 0.0D;
int eatenLength = 3;
if (params.size()>3+offset && optionalDirection)
{
yaw = NumericValue.asNumber(params.get(3 + offset).evalValue(c)).getDouble();
pitch = NumericValue.asNumber(params.get(4 + offset).evalValue(c)).getDouble();
eatenLength = 5;
}
return new Vector3Argument(pos,offset+eatenLength, yaw, pitch);
}
catch (IndexOutOfBoundsException e)
{
throw new InternalExpressionException("Position argument should be defined either by three coordinates (a triple or by three arguments), or a positioned block value");
}
}
public static Vector3Argument findIn(List<Value> params, int offset, boolean optionalDirection)
{
try
{
Value v1 = params.get(0 + offset);
if (v1 instanceof BlockValue)
{
return (new Vector3Argument(new Vec3d(((BlockValue) v1).getPos()).add(0.5,0.5,0.5), 1+offset)).fromBlock();
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
Vec3d pos = new Vec3d(
NumericValue.asNumber(args.get(0)).getDouble(),
NumericValue.asNumber(args.get(1)).getDouble(),
NumericValue.asNumber(args.get(2)).getDouble());
double yaw = 0.0D;
double pitch = 0.0D;
if (args.size()>3 && optionalDirection)
{
yaw = NumericValue.asNumber(args.get(3)).getDouble();
pitch = NumericValue.asNumber(args.get(4)).getDouble();
}
return new Vector3Argument(pos,offset+1, yaw, pitch);
}
Vec3d pos = new Vec3d(
NumericValue.asNumber(v1).getDouble(),
NumericValue.asNumber(params.get(1 + offset)).getDouble(),
NumericValue.asNumber(params.get(2 + offset)).getDouble());
double yaw = 0.0D;
double pitch = 0.0D;
int eatenLength = 3;
if (params.size()>3+offset && optionalDirection)
{
yaw = NumericValue.asNumber(params.get(3 + offset)).getDouble();
pitch = NumericValue.asNumber(params.get(4 + offset)).getDouble();
eatenLength = 5;
}
return new Vector3Argument(pos,offset+eatenLength, yaw, pitch);
}
catch (IndexOutOfBoundsException e)
{
throw new InternalExpressionException("Position argument should be defined either by three coordinates (a triple or by three arguments), or a positioned block value");
}
}
}
@@ -1,17 +1,24 @@
package carpet.script.bundled;
import carpet.CarpetServer;
import net.minecraft.nbt.PositionTracker;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.TagReaders;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class FileModule extends Module
@@ -74,7 +81,7 @@ public class FileModule extends Module
}
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
public static void write(Tag tag_1, File file)
public static boolean write(Tag tag_1, File file)
{
File file_2 = new File(file.getAbsolutePath() + "_tmp");
if (file_2.exists()) file_2.delete();
@@ -90,9 +97,50 @@ public class FileModule extends Module
}
catch (IOException e)
{
return;
return false;
}
if (file.exists()) file.delete();
if (!file.exists()) file_2.renameTo(file);
return true;
}
public static boolean appendText(File filePath, boolean addNewLines, List<String> data)
{
try
{
OutputStream out = Files.newOutputStream(filePath.toPath(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
{
for (String line: data)
{
writer.append(line);
if (addNewLines) writer.newLine();
}
}
}
catch (IOException e)
{
return false;
}
return true;
}
public static List<String> listFileContent(File filePath)
{
try (BufferedReader reader = Files.newBufferedReader(filePath.toPath(), StandardCharsets.UTF_8)) {
List<String> result = new ArrayList<>();
for (;;) {
String line = reader.readLine();
if (line == null)
break;
result.add(line.replaceAll("[\n\r]+",""));
}
return result;
}
catch (IOException e)
{
return null;
}
}
}
+57 -23
View File
@@ -4,42 +4,76 @@ import carpet.CarpetServer;
import net.minecraft.nbt.Tag;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import static carpet.script.bundled.FileModule.read;
import static carpet.script.bundled.FileModule.write;
import java.util.List;
public abstract class Module
{
public abstract String getName();
public abstract String getCode();
public abstract boolean isLibrary();
public final static Object writeIOSync = new Object();
public Tag getData(String file)
private static String getDescriptor(Module module, String file, boolean isShared)
{
File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
return read(dataFile);
if (isShared)
{
return "shared/"+file;
}
else if (module != null && module.getName() != null) // appdata
{
return module.getName()+".data"+(file==null?"":"/"+file);
}
else
{
throw new RuntimeException("Invalid file descriptor: "+file);
}
}
public void saveData(String file, Tag globalState)
public static Tag getData(Module module, String file, boolean isShared)
{
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
if (!Files.exists(dataFile.toPath().getParent()))
{
try
{
Files.createDirectory(dataFile.toPath().getParent());
}
catch (IOException ignored)
{
}
}
write(globalState, dataFile);
File dataFile = resolveResource(module, file, "nbt", isShared);
if (dataFile == null) return null;
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
synchronized (writeIOSync) { return FileModule.read(dataFile); }
}
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
{
File dataFile = resolveResource(module, file, "nbt", isShared);
if (dataFile == null) return false;
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.write(globalState, dataFile); }
}
public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List<String> message)
{
File dataFile = resolveResource(module, resourceName, "txt", isShared);
if (dataFile == null) return false;
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.appendText(dataFile, type.equals("text"), message); }
}
public static boolean dropExistingFile(Module module, String resourceName, String ext, boolean isShared)
{
File dataFile = resolveResource(module, resourceName, ext, isShared);
if (dataFile == null) return false;
synchronized (writeIOSync) { return dataFile.delete(); }
}
public static List<String> listFile(Module module, String resourceName, String ext, boolean isShared)
{
File dataFile = resolveResource(module, resourceName, ext, isShared);
if (dataFile == null) return null;
if (!dataFile.exists()) return null;
synchronized (writeIOSync) { return FileModule.listFileContent(dataFile); }
}
private static File resolveResource(Module module, String resourceName, String ext, boolean isShared)
{
if (!isShared && (module == null || module.getName() == null)) return null;
return CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getDescriptor(module, resourceName, isShared)+"."+ext);
}
@Override
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,790 @@
package carpet.script.utils;
import carpet.CarpetSettings;
import com.mojang.blaze3d.systems.RenderSystem;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IWorld;
import net.minecraft.world.dimension.DimensionType;
import org.lwjgl.opengl.GL11;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class ShapesRenderer
{
private final Map<DimensionType, Long2ObjectOpenHashMap<RenderedShape<? extends ShapeDispatcher.ExpiringShape>>> shapes;
private MinecraftClient client;
private Map<String, BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape >>> renderedShapes
= new HashMap<String, BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape>>>()
{{
put("line", RenderedLine::new);
put("box", RenderedBox::new);
put("sphere", RenderedSphere::new);
put("cylinder", RenderedCylinder::new);
}};
public ShapesRenderer(MinecraftClient minecraftClient)
{
this.client = minecraftClient;
shapes = new HashMap<>();
shapes.put(DimensionType.OVERWORLD, new Long2ObjectOpenHashMap<>());
shapes.put(DimensionType.THE_NETHER, new Long2ObjectOpenHashMap<>());
shapes.put(DimensionType.THE_END, new Long2ObjectOpenHashMap<>());
}
public void render(Camera camera, float partialTick)
{
IWorld iWorld = this.client.world;
DimensionType dimensionType = iWorld.getDimension().getType();
if (shapes.get(dimensionType).isEmpty()) return;
long currentTime = client.world.getTime();
RenderSystem.disableTexture();
RenderSystem.enableDepthTest();
RenderSystem.depthFunc(515);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
//RenderSystem.blendFuncSeparate(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE, GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ZERO);
//RenderSystem.shadeModel(7425);
RenderSystem.shadeModel(GL11.GL_FLAT);
RenderSystem.enableAlphaTest();
RenderSystem.defaultAlphaFunc();
//RenderSystem.alphaFunc(GL11.GL_GREATER, 0.005F);
RenderSystem.disableCull();
RenderSystem.disableLighting();
RenderSystem.depthMask(false);
//RenderSystem.polygonOffset(-3f, -3f);
//RenderSystem.enablePolygonOffset();
//Entity entity = this.client.gameRenderer.getCamera().getFocusedEntity();
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
// render
double cameraX = camera.getPos().x;
double cameraY = camera.getPos().y;
double cameraZ = camera.getPos().z;
synchronized (shapes)
{
shapes.get(dimensionType).long2ObjectEntrySet().removeIf(
entry -> entry.getValue().isExpired(currentTime)
);
shapes.get(dimensionType).values().forEach(
s ->
{
if ( s.shouldRender(dimensionType))
s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick);
}
);
//lines
shapes.get(dimensionType).values().forEach(
s -> {
if ( s.shouldRender(dimensionType))
s.renderLines(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick);
}
);
}
RenderSystem.enableCull();
RenderSystem.depthMask(true);
RenderSystem.lineWidth(1.0F);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableTexture();
RenderSystem.shadeModel(7424);
}
public void addShapes(ListTag tag)
{
for (int i=0, count = tag.size(); i < count; i++)
{
addShape(tag.getCompound(i));
}
}
public void addShape(CompoundTag tag)
{
ShapeDispatcher.ExpiringShape shape = ShapeDispatcher.fromTag(tag);
if (shape == null) return;
BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape >> shapeFactory;
shapeFactory = renderedShapes.get(tag.getString("shape"));
if (shapeFactory == null)
{
CarpetSettings.LOG.info("Unrecognized shape: "+tag.getString("shape"));
}
else
{
RenderedShape<?> rshape = shapeFactory.apply(client, shape);
DimensionType dim = shape.shapeDimension;
long key = rshape.key();
synchronized (shapes)
{
RenderedShape<?> existing = shapes.get(dim).get(key);
if (existing != null)
{ // promoting previous shape
existing.expiryTick = rshape.expiryTick;
}
else
{
shapes.get(dim).put(key, rshape);
}
}
}
}
public void reset()
{
synchronized (shapes)
{
shapes.values().forEach(Long2ObjectOpenHashMap::clear);
}
}
public abstract static class RenderedShape<T extends ShapeDispatcher.ExpiringShape>
{
protected T shape;
protected MinecraftClient client;
long expiryTick;
double renderEpsilon = 0;
public abstract void renderLines(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz, float partialTick );
public void renderFaces(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz, float partialTick ) {}
protected RenderedShape(MinecraftClient client, T shape)
{
this.shape = shape;
expiryTick = client.world.getTime()+shape.getExpiry();
renderEpsilon = (3+((double)shape.key())/Long.MAX_VALUE)/1000;
this.client = client;
}
public boolean isExpired(long currentTick)
{
return expiryTick < currentTick;
}
public long key()
{
return shape.key();
};
public boolean shouldRender(DimensionType dim)
{
if (shape.followEntity <=0 ) return true;
if (client.world == null) return false;
if (client.world.getEntityById(shape.followEntity) == null) return false;
return true;
}
}
public static class RenderedBox extends RenderedShape<ShapeDispatcher.Box>
{
private RenderedBox(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
{
super(client, (ShapeDispatcher.Box)shape);
}
@Override
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
{
if (shape.a == 0.0) return;
Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
RenderSystem.lineWidth(shape.lineWidth);
drawBoxWireGLLines(tessellator, bufferBuilder,
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon),
(float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon),
v1.x!=v2.x, v1.y!=v2.y, v1.z!=v2.z,
shape.r, shape.g, shape.b, shape.a, shape.r, shape.g, shape.b
);
}
@Override
public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
{
if (shape.fa == 0.0) return;
Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
RenderSystem.lineWidth(1.0F);
drawBoxFaces(tessellator, bufferBuilder,
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon),
(float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon),
v1.x!=v2.x, v1.y!=v2.y, v1.z!=v2.z,
shape.fr, shape.fg, shape.fb, shape.fa
);
}
}
public static class RenderedLine extends RenderedShape<ShapeDispatcher.Line>
{
public RenderedLine(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
{
super(client, (ShapeDispatcher.Line)shape);
}
@Override
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
{
Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
RenderSystem.lineWidth(shape.lineWidth);
drawLine(tessellator, bufferBuilder,
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon),
(float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon),
shape.r, shape.g, shape.b, shape.a
);
}
}
public static class RenderedSphere extends RenderedShape<ShapeDispatcher.Sphere>
{
public RenderedSphere(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
{
super(client, (ShapeDispatcher.Sphere)shape);
}
@Override
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
{
if (shape.a == 0.0) return;
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
RenderSystem.lineWidth(shape.lineWidth);
drawSphereWireframe(tessellator, bufferBuilder,
(float)(vc.x-cx), (float)(vc.y-cy), (float)(vc.z-cz),
(float)(shape.radius+renderEpsilon), shape.subdivisions,
shape.r, shape.g, shape.b, shape.a);
}
@Override
public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
{
if (shape.fa == 0.0) return;
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
RenderSystem.lineWidth(1.0f);
drawSphereFaces(tessellator, bufferBuilder,
(float)(vc.x-cx), (float)(vc.y-cy), (float)(vc.z-cz),
(float)(shape.radius+renderEpsilon), shape.subdivisions,
shape.fr, shape.fg, shape.fb, shape.fa);
}
}
public static class RenderedCylinder extends RenderedShape<ShapeDispatcher.Cylinder>
{
public RenderedCylinder(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
{
super(client, (ShapeDispatcher.Cylinder)shape);
}
@Override
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
{
if (shape.a == 0.0) return;
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
RenderSystem.lineWidth(shape.lineWidth);
double dir = MathHelper.sign(shape.height);
drawCylinderWireframe(tessellator, bufferBuilder,
(float) (vc.x - cx - dir * renderEpsilon), (float) (vc.y - cy - dir * renderEpsilon), (float) (vc.z - cz - dir * renderEpsilon),
(float) (shape.radius + renderEpsilon), (float) (shape.height + 2*dir*renderEpsilon), shape.axis,
shape.subdivisions, shape.radius == 0,
shape.r, shape.g, shape.b, shape.a);
}
@Override
public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
{
if (shape.fa == 0.0) return;
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
RenderSystem.lineWidth(1.0f);
double dir = MathHelper.sign(shape.height);
drawCylinderFaces(tessellator, bufferBuilder,
(float) (vc.x - cx - dir * renderEpsilon), (float) (vc.y - cy - dir * renderEpsilon), (float) (vc.z - cz - dir * renderEpsilon),
(float) (shape.radius + renderEpsilon), (float) (shape.height + 2*dir*renderEpsilon), shape.axis,
shape.subdivisions, shape.radius == 0,
shape.fr, shape.fg, shape.fb, shape.fa);
}
}
// some raw shit
public static void drawLine(Tessellator tessellator, BufferBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2, float red1, float grn1, float blu1, float alpha) {
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
tessellator.draw();
}
public static void drawBoxWireGLLines(
Tessellator tessellator, BufferBuilder builder,
float x1, float y1, float z1,
float x2, float y2, float z2,
boolean xthick, boolean ythick, boolean zthick,
float red1, float grn1, float blu1, float alpha, float red2, float grn2, float blu2)
{
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3
if (xthick)
{
builder.vertex(x1, y1, z1).color(red1, grn2, blu2, alpha).next();
builder.vertex(x2, y1, z1).color(red1, grn2, blu2, alpha).next();
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
}
if (ythick)
{
builder.vertex(x1, y1, z1).color(red2, grn1, blu2, alpha).next();
builder.vertex(x1, y2, z1).color(red2, grn1, blu2, alpha).next();
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
}
if (zthick)
{
builder.vertex(x1, y1, z1).color(red2, grn2, blu1, alpha).next();
builder.vertex(x1, y1, z2).color(red2, grn2, blu1, alpha).next();
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
}
tessellator.draw();
}
public static void drawBoxFaces(
Tessellator tessellator, BufferBuilder builder,
float x1, float y1, float z1,
float x2, float y2, float z2,
boolean xthick, boolean ythick, boolean zthick,
float red1, float grn1, float blu1, float alpha)
{
builder.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR);
if (xthick && ythick)
{
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
if (zthick)
{
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
}
}
if (zthick && ythick)
{
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
if (xthick)
{
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
}
}
// now at least drawing one
if (zthick && xthick)
{
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
if (ythick)
{
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
}
}
tessellator.draw();
}
public static void drawCylinderWireframe(Tessellator tessellator, BufferBuilder builder,
float cx, float cy, float cz,
float r, float h, Direction.Axis axis, int subd, boolean isFlat,
float red, float grn, float blu, float alpha)
{
float step = (float)Math.PI / (subd/2);
int num_steps180 = (int)(Math.PI / step)+1;
int num_steps360 = (int)(2*Math.PI / step);
int hsteps = 1;
float hstep = 1.0f;
if (!isFlat)
{
hsteps = (int) Math.ceil(MathHelper.abs(h) / (step * r)) + 1;
hstep = h / (hsteps - 1);
}// draw base
if (axis == Direction.Axis.Y)
{
for (int dh = 0; dh < hsteps; dh++)
{
float hh = dh*hstep;
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float y = hh;
float z = r * MathHelper.sin(theta);
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
if (!isFlat)
{
for (int i = 0; i <= num_steps180; i++)
{
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
float theta = step * i;
float x = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx - x, cy + 0, cz + z).color(red, grn, blu, alpha).next();
builder.vertex(cx + x, cy + 0, cz - z).color(red, grn, blu, alpha).next();
builder.vertex(cx + x, cy + h, cz - z).color(red, grn, blu, alpha).next();
builder.vertex(cx - x, cy + h, cz + z).color(red, grn, blu, alpha).next();
tessellator.draw();
}
}
else
{
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps180; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx - x, cy, cz + z).color(red, grn, blu, alpha).next();
builder.vertex(cx + x, cy, cz - z).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
else if (axis == Direction.Axis.X)
{
for (int dh = 0; dh < hsteps; dh++)
{
float hh = dh * hstep;
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float z = r * MathHelper.cos(theta);
float x = hh;
float y = r * MathHelper.sin(theta);
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
if (!isFlat)
{
for (int i = 0; i <= num_steps180; i++)
{
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
float theta = step * i;
float y = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx + 0, cy - y, cz + z).color(red, grn, blu, alpha).next();
builder.vertex(cx + 0, cy + y, cz - z).color(red, grn, blu, alpha).next();
builder.vertex(cx + h, cy + y, cz - z).color(red, grn, blu, alpha).next();
builder.vertex(cx + h, cy - y, cz + z).color(red, grn, blu, alpha).next();
tessellator.draw();
}
}
else
{
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps180; i++)
{
float theta = step * i;
float y = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx, cy - y, cz + z).color(red, grn, blu, alpha).next();
builder.vertex(cx, cy + y, cz - z).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
else if (axis == Direction.Axis.Z)
{
for (int dh = 0; dh < hsteps; dh++)
{
float hh = dh * hstep;
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float y = r * MathHelper.cos(theta);
float z = hh;
float x = r * MathHelper.sin(theta);
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
if (!isFlat)
{
for (int i = 0; i <= num_steps180; i++)
{
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
float theta = step * i;
float x = r * MathHelper.cos(theta);
float y = r * MathHelper.sin(theta);
builder.vertex(cx + x, cy - y, cz + 0).color(red, grn, blu, alpha).next();
builder.vertex(cx - x, cy + y, cz + 0).color(red, grn, blu, alpha).next();
builder.vertex(cx - x, cy + y, cz + h).color(red, grn, blu, alpha).next();
builder.vertex(cx + x, cy - y, cz + h).color(red, grn, blu, alpha).next();
tessellator.draw();
}
}
else
{
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps180; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float y = r * MathHelper.sin(theta);
builder.vertex(cx + x, cy - y, cz).color(red, grn, blu, alpha).next();
builder.vertex(cx - x, cy + y, cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
}
public static void drawCylinderFaces(Tessellator tessellator, BufferBuilder builder,
float cx, float cy, float cz,
float r, float h, Direction.Axis axis, int subd, boolean isFlat,
float red, float grn, float blu, float alpha)
{
float step = (float)Math.PI / (subd/2);
int num_steps180 = (int)(Math.PI / step)+1;
int num_steps360 = (int)(2*Math.PI / step)+1;
if (axis == Direction.Axis.Y)
{
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
builder.vertex(cx, cy, cz).color(red, grn, blu, alpha).next();
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(x + cx, cy, z + cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
if (!isFlat)
{
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
builder.vertex(cx, cy+h, cz).color(red, grn, blu, alpha).next();
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(x + cx, cy+h, z + cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx + x, cy + 0, cz + z).color(red, grn, blu, alpha).next();
builder.vertex(cx + x, cy + h, cz + z).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
else if (axis == Direction.Axis.X)
{
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
builder.vertex(cx, cy, cz).color(red, grn, blu, alpha).next();
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float y = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx, cy + y, z + cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
if (!isFlat)
{
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
builder.vertex(cx+h, cy, cz).color(red, grn, blu, alpha).next();
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float y = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx+h, cy + y, cz + z).color(red, grn, blu, alpha).next();
}
tessellator.draw();
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float y = r * MathHelper.cos(theta);
float z = r * MathHelper.sin(theta);
builder.vertex(cx + 0, cy + y, cz + z).color(red, grn, blu, alpha).next();
builder.vertex(cx + h, cy + y, cz + z).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
else if (axis == Direction.Axis.Z)
{
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
builder.vertex(cx, cy, cz).color(red, grn, blu, alpha).next();
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float y = r * MathHelper.sin(theta);
builder.vertex(x + cx, cy+y, cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
if (!isFlat)
{
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
builder.vertex(cx, cy, cz+h).color(red, grn, blu, alpha).next();
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float y = r * MathHelper.sin(theta);
builder.vertex(x + cx, cy+y, cz+h).color(red, grn, blu, alpha).next();
}
tessellator.draw();
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.cos(theta);
float y = r * MathHelper.sin(theta);
builder.vertex(cx + x, cy + y, cz + 0).color(red, grn, blu, alpha).next();
builder.vertex(cx + x, cy + y, cz + h).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
}
public static void drawSphereWireframe(Tessellator tessellator, BufferBuilder builder,
float cx, float cy, float cz,
float r, int subd,
float red, float grn, float blu, float alpha)
{
float step = (float)Math.PI / (subd/2);
int num_steps180 = (int)(Math.PI / step)+1;
int num_steps360 = (int)(2*Math.PI / step);
for (int i = 0; i <= num_steps360; i++)
{
builder.begin(GL11.GL_LINE_STRIP, VertexFormats.POSITION_COLOR);
float theta = step * i ;
for (int j = 0; j <= num_steps180; j++)
{
float phi = step * j ;
float x = r * MathHelper.sin(phi) * MathHelper.cos(theta);
float z = r * MathHelper.sin(phi) * MathHelper.sin(theta);
float y = r * MathHelper.cos(phi);
builder.vertex(x+cx, y+cy, z+cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
for (int j = 0; j <= num_steps180; j++)
{
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
float phi = step * j ;
for (int i = 0; i <= num_steps360; i++)
{
float theta = step * i;
float x = r * MathHelper.sin(phi) * MathHelper.cos(theta);
float z = r * MathHelper.sin(phi) * MathHelper.sin(theta);
float y = r * MathHelper.cos(phi);
builder.vertex(x+cx, y+cy, z+cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
public static void drawSphereFaces(Tessellator tessellator, BufferBuilder builder,
float cx, float cy, float cz,
float r, int subd,
float red, float grn, float blu, float alpha)
{
float step = (float)Math.PI / (subd/2);
int num_steps180 = (int)(Math.PI / step)+1;
int num_steps360 = (int)(2*Math.PI / step);
for (int i = 0; i <= num_steps360; i++)
{
float theta = i * step;
float thetaprime = theta+step;
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
for (int j = 0; j <= num_steps180; j++)
{
float phi = j * step;
float x = r * MathHelper.sin(phi) * MathHelper.cos(theta);
float z = r * MathHelper.sin(phi) * MathHelper.sin(theta);
float y = r * MathHelper.cos(phi);
float xp = r * MathHelper.sin(phi) * MathHelper.cos(thetaprime);
float zp = r * MathHelper.sin(phi) * MathHelper.sin(thetaprime);
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
builder.vertex(xp + cx, y + cy, zp + cz).color(red, grn, blu, alpha).next();
}
tessellator.draw();
}
}
}
@@ -72,172 +72,11 @@ public class BlockValue extends Value
throw new InternalExpressionException("Cannot parse block: "+str);
}
public static VectorLocator locateVec(CarpetContext c, List<LazyValue> params, int offset)
{
return locateVec(c,params, offset, false);
}
public static VectorLocator locateVec(CarpetContext c, List<LazyValue> params, int offset, boolean optionalDirection)
{
try
{
Value v1 = params.get(0 + offset).evalValue(c);
if (v1 instanceof BlockValue)
{
return (new VectorLocator(new Vec3d(((BlockValue) v1).getPos()).add(0.5,0.5,0.5), 1+offset)).fromBlock();
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
Vec3d pos = new Vec3d(
NumericValue.asNumber(args.get(0)).getDouble(),
NumericValue.asNumber(args.get(1)).getDouble(),
NumericValue.asNumber(args.get(2)).getDouble());
double yaw = 0.0D;
double pitch = 0.0D;
if (args.size()>3 && optionalDirection)
{
yaw = NumericValue.asNumber(args.get(3)).getDouble();
pitch = NumericValue.asNumber(args.get(4)).getDouble();
}
return new VectorLocator(pos,offset+1, yaw, pitch);
}
Vec3d pos = new Vec3d(
NumericValue.asNumber(v1).getDouble(),
NumericValue.asNumber(params.get(1 + offset).evalValue(c)).getDouble(),
NumericValue.asNumber(params.get(2 + offset).evalValue(c)).getDouble());
double yaw = 0.0D;
double pitch = 0.0D;
int eatenLength = 3;
if (params.size()>3+offset && optionalDirection)
{
yaw = NumericValue.asNumber(params.get(3 + offset).evalValue(c)).getDouble();
pitch = NumericValue.asNumber(params.get(4 + offset).evalValue(c)).getDouble();
eatenLength = 5;
}
return new VectorLocator(pos,offset+eatenLength, yaw, pitch);
}
catch (IndexOutOfBoundsException e)
{
throw new InternalExpressionException("Position should be defined either by three coordinates, or a block value");
}
}
public static BlockPos locateBlockPos(CarpetContext c, int xpos, int ypos, int zpos)
{
return new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos);
}
public static LocatorResult fromParams(CarpetContext c, List<LazyValue> params, int offset)
{
return fromParams(c, params,offset, false, false);
}
public static LocatorResult fromParams(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString)
{
return fromParams(c, params,offset, acceptString, false);
}
public static LocatorResult fromParams(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString, boolean optional)
{
try
{
Value v1 = params.get(0 + offset).evalValue(c);
//add conditional from string name
if (optional && v1 instanceof NullValue)
{
return new LocatorResult(null, 1+offset);
}
if (acceptString && v1 instanceof StringValue)
{
return new LocatorResult(fromString(v1.getString()), 1+offset);
}
if (v1 instanceof BlockValue)
{
return new LocatorResult(((BlockValue) v1), 1+offset);
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
return new LocatorResult(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
1+offset);
}
int xpos = (int) NumericValue.asNumber(v1).getLong();
int ypos = (int) NumericValue.asNumber( params.get(1 + offset).evalValue(c)).getLong();
int zpos = (int) NumericValue.asNumber( params.get(2 + offset).evalValue(c)).getLong();
return new LocatorResult(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
3+offset
);
}
catch (IndexOutOfBoundsException e)
{
throw new InternalExpressionException("Block should be defined either by three coordinates, a block value, or a proper string");
}
}
public static LocatorResult fromParamValues(CarpetContext c, List<Value> params, int offset, boolean acceptString, boolean optional)
{
try
{
Value v1 = params.get(0 + offset);
//add conditional from string name
if (optional && v1 instanceof NullValue)
{
return new LocatorResult(null, 1+offset);
}
if (acceptString && v1 instanceof StringValue)
{
return new LocatorResult(fromString(v1.getString()), 1+offset);
}
if (v1 instanceof BlockValue)
{
return new LocatorResult(((BlockValue) v1), 1+offset);
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
return new LocatorResult(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
1+offset);
}
int xpos = (int) NumericValue.asNumber(v1).getLong();
int ypos = (int) NumericValue.asNumber( params.get(1 + offset)).getLong();
int zpos = (int) NumericValue.asNumber( params.get(2 + offset)).getLong();
return new LocatorResult(
new BlockValue(
null,
c.s.getWorld(),
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
),
3+offset
);
}
catch (IndexOutOfBoundsException e)
{
throw new InternalExpressionException("Block should be defined either by three coordinates, a block value, or a proper string");
}
}
public BlockState getBlockState()
{
if (blockState != null)
@@ -342,46 +181,6 @@ public class BlockValue extends Value
public ServerWorld getWorld() { return world;}
public static class LocatorResult
{
public final BlockValue block;
public final int offset;
LocatorResult(BlockValue b, int o)
{
block = b;
offset = o;
}
}
public static class VectorLocator
{
public Vec3d vec;
public final int offset;
public final double yaw;
public final double pitch;
public boolean fromBlock;
VectorLocator(Vec3d v, int o)
{
vec = v;
offset = o;
yaw = 0.0D;
pitch = 0.0D;
}
VectorLocator(Vec3d v, int o, double y, double p)
{
vec = v;
offset = o;
yaw = y;
pitch = p;
}
public VectorLocator fromBlock()
{
fromBlock = true;
return this;
}
}
public enum SpecificDirection {
UP("up",0.5, 0.0, 0.5, Direction.UP),
@@ -1,15 +1,21 @@
package carpet.script.value;
import carpet.CarpetSettings;
import carpet.fakes.EntityInterface;
import carpet.fakes.ItemEntityInterface;
import carpet.fakes.LivingEntityInterface;
import carpet.fakes.MobEntityInterface;
import carpet.fakes.HungerManagerInterface;
import carpet.helpers.Tracer;
import carpet.patches.EntityPlayerMPFake;
import carpet.script.CarpetContext;
import carpet.script.EntityEventsGroup;
import carpet.script.argument.Vector3Argument;
import carpet.script.exception.InternalExpressionException;
import carpet.utils.Messenger;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.player.HungerManager;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
@@ -45,6 +51,7 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.GameMode;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
@@ -60,6 +67,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import static carpet.script.value.NBTSerializableValue.nameFromRegistryId;
import static carpet.utils.MobAI.genericJump;
// TODO: decide whether copy(entity) should duplicate entity in the world.
public class EntityValue extends Value
@@ -118,7 +126,7 @@ public class EntityValue extends Value
@Override
public String getString()
{
return entity.getDisplayName().getString();
return entity.getName().getString();
}
@Override
@@ -220,7 +228,9 @@ public class EntityValue extends Value
put("motion_x", (e, a) -> new NumericValue(e.getVelocity().x));
put("motion_y", (e, a) -> new NumericValue(e.getVelocity().y));
put("motion_z", (e, a) -> new NumericValue(e.getVelocity().z));
put("name", (e, a) -> new StringValue(e.getDisplayName().getString()));
put("name", (e, a) -> new StringValue(e.getName().getString()));
put("display_name", (e, a) -> new StringValue(e.getDisplayName().getString()));
put("command_name", (e, a) -> new StringValue(e.getEntityName()));
put("custom_name", (e, a) -> e.hasCustomName()?new StringValue(e.getCustomName().getString()):Value.NULL);
put("type", (e, a) -> new StringValue(nameFromRegistryId(Registry.ENTITY_TYPE.getId(e.getType()))));
put("is_riding", (e, a) -> new NumericValue(e.hasVehicle()));
@@ -252,7 +262,9 @@ public class EntityValue extends Value
put("despawn_timer", (e, a) -> e instanceof LivingEntity?new NumericValue(((LivingEntity) e).getDespawnCounter()):Value.NULL);
put("item", (e, a) -> (e instanceof ItemEntity)?ListValue.fromItemStack(((ItemEntity) e).getStack()):Value.NULL);
put("count", (e, a) -> (e instanceof ItemEntity)?new NumericValue(((ItemEntity) e).getStack().getCount()):Value.NULL);
put("pickup_delay", (e, a) -> (e instanceof ItemEntity)?new NumericValue(((ItemEntityInterface) e).getPickupDelay()):Value.NULL);
put("pickup_delay", (e, a) -> (e instanceof ItemEntity)?new NumericValue(((ItemEntityInterface) e).getPickupDelayCM()):Value.NULL);
put("portal_cooldown", (e , a) ->new NumericValue(((EntityInterface)e).getPortalTimer()));
put("portal_timer", (e , a) ->new NumericValue(e.netherPortalCooldown));
// ItemEntity -> despawn timer via ssGetAge
put("is_baby", (e, a) -> (e instanceof LivingEntity)?new NumericValue(((LivingEntity) e).isBaby()):Value.NULL);
put("target", (e, a) -> {
@@ -277,13 +289,27 @@ public class EntityValue extends Value
put("sneaking", (e, a) -> e.isSneaking()?Value.TRUE:Value.FALSE);
put("sprinting", (e, a) -> e.isSprinting()?Value.TRUE:Value.FALSE);
put("swimming", (e, a) -> e.isSwimming()?Value.TRUE:Value.FALSE);
/*put("jumping", (e, a) -> {
put("hunger", (e, a) -> {
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getFoodLevel());
return Value.NULL;
});
put("saturation", (e, a) -> {
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getSaturationLevel());
return Value.NULL;
});
put("exhaustion",(e, a)->{
if(e instanceof PlayerEntity) return new NumericValue(((HungerManagerInterface)((PlayerEntity) e).getHungerManager()).getExhaustionCM());
return Value.NULL;
});
put("jumping", (e, a) -> {
if (e instanceof LivingEntity)
{
return ((LivingEntity) e).getJumping()?Value.TRUE:Value.FALSE;
return ((LivingEntityInterface) e).isJumpingCM()?Value.TRUE:Value.FALSE;
}
return Value.NULL;
});*/ //needs mixing
});
put("gamemode", (e, a) -> {
if (e instanceof ServerPlayerEntity)
{
@@ -331,6 +357,8 @@ public class EntityValue extends Value
return Value.NULL;
});
put("team", (e, a) -> e.getScoreboardTeam()==null?Value.NULL:new StringValue(e.getScoreboardTeam().getName()));
put("ping", (e, a) -> {
if (e instanceof ServerPlayerEntity)
{
@@ -370,6 +398,7 @@ public class EntityValue extends Value
StatusEffectInstance pe = ((LivingEntity) e).getStatusEffect(potion);
return ListValue.of( new NumericValue(pe.getAmplifier()), new NumericValue(pe.getDuration()) );
});
put("health", (e, a) ->
{
if (e instanceof LivingEntity)
@@ -475,6 +504,8 @@ public class EntityValue extends Value
return new NBTSerializableValue(nbttagcompound);
return new NBTSerializableValue(nbttagcompound).get(a);
});
put("category",(e,a)->{return new StringValue(e.getType().getCategory().toString().toLowerCase(Locale.ROOT));});
}};
public void set(String what, Value toWhat)
@@ -523,12 +554,40 @@ public class EntityValue extends Value
//((ServerWorld)e.getEntityWorld()).method_14178().sendToNearbyPlayers(e, new EntityVelocityUpdateS2CPacket(e));
}
private static final Map<String, BiConsumer<Entity, Value>> featureModifiers = new HashMap<String, BiConsumer<Entity, Value>>() {{
put("remove", (entity, value) -> entity.remove());
put("age", (e, v) -> e.age = Math.abs((int)NumericValue.asNumber(v).getLong()) );
put("health", (e, v) -> { if (e instanceof LivingEntity) ((LivingEntity) e).setHealth((float) NumericValue.asNumber(v).getDouble()); });
put("health", (e, v) -> {
float health = (float) NumericValue.asNumber(v).getDouble();
if (health <= 0f && e instanceof ServerPlayerEntity)
{
ServerPlayerEntity player = (ServerPlayerEntity) e;
if (player.container != null)
{
// if player dies with open container, then that causes NPE on the client side
// its a client side bug that may never surface unless vanilla gets into scripting at some point
// bug: #228
player.closeContainer();
}
((LivingEntity) e).setHealth(health);
}
if (e instanceof LivingEntity) ((LivingEntity) e).setHealth(health);
});
// todo add handling of the source for extra effects
/*put("damage", (e, v) -> {
float dmgPoints;
DamageSource source;
if (v instanceof ListValue && ((ListValue) v).getItems().size() > 1)
{
List<Value> vals = ((ListValue) v).getItems();
dmgPoints = (float) NumericValue.asNumber(v).getDouble();
source = DamageSource ... yeah...
}
else
{
}
});*/
put("kill", (e, v) -> e.kill());
put("location", (e, v) ->
{
@@ -751,26 +810,12 @@ public class EntityValue extends Value
else if (v instanceof ListValue)
{
List<Value> lv = ((ListValue) v).getItems();
if (lv.get(0) instanceof BlockValue)
Vector3Argument locator = Vector3Argument.findIn(lv, 0, false);
pos = new BlockPos(locator.vec.x, locator.vec.y, locator.vec.z);
if (lv.size() > locator.offset)
{
pos = ((BlockValue) lv.get(0)).getPos();
if (lv.size()>1)
{
distance = (int) NumericValue.asNumber(lv.get(1)).getLong();
}
distance = (int) NumericValue.asNumber(lv.get(locator.offset)).getLong();
}
else if (lv.size()>=3)
{
pos = new BlockPos(NumericValue.asNumber(lv.get(0)).getLong(),
NumericValue.asNumber(lv.get(1)).getLong(),
NumericValue.asNumber(lv.get(2)).getLong());
if (lv.size()>3)
{
distance = (int) NumericValue.asNumber(lv.get(4)).getLong();
}
}
else throw new InternalExpressionException("'home' requires at least one position argument, and optional distance");
}
else throw new InternalExpressionException("'home' requires at least one position argument, and optional distance");
@@ -800,6 +845,20 @@ public class EntityValue extends Value
}
});
put("portal_cooldown", (e , v) ->
{
if (v==null)
throw new InternalExpressionException("'portal_cooldown' requires a value to set");
e.netherPortalCooldown = NumericValue.asNumber(v).getInt();
});
put("portal_timer", (e , v) ->
{
if (v==null)
throw new InternalExpressionException("'portal_timer' requires a value to set");
((EntityInterface) e).setPortalTimer(NumericValue.asNumber(v).getInt());
});
put("ai", (e, v) ->
{
if (e instanceof MobEntity)
@@ -820,7 +879,10 @@ public class EntityValue extends Value
if (!(e instanceof LivingEntity)) return;
LivingEntity le = (LivingEntity)e;
if (v == null)
{
le.clearStatusEffects();
return;
}
else if (v instanceof ListValue)
{
List<Value> lv = ((ListValue) v).getItems();
@@ -840,7 +902,7 @@ public class EntityValue extends Value
{
le.removeStatusEffect(effect);
return;
}
}
int amplifier = 0;
if (lv.size() > 2)
amplifier = (int)NumericValue.asNumber(lv.get(2)).getLong();
@@ -854,24 +916,81 @@ public class EntityValue extends Value
return;
}
}
else
{
String effectName = v.getString();
StatusEffect effect = Registry.STATUS_EFFECT.get(new Identifier(effectName));
if (effect == null)
throw new InternalExpressionException("Wrong effect name: "+effectName);
le.removeStatusEffect(effect);
return;
}
throw new InternalExpressionException("'effect' needs either no arguments (clear) or effect name, duration, and optional amplifier, show particles and show icon");
});
// gamemode
// spectate
// "fire"
// "extinguish"
// "silent"
// "gravity"
// "invulnerable"
// "dimension"
// "item"
// "count",
// "age",
// "effect_"name
// "hold"
// "hold_offhand"
// "jump"
put("gamemode", (e,v)->{
if(!(e instanceof ServerPlayerEntity)) return;
GameMode toSet = v instanceof NumericValue ?
GameMode.byId(((NumericValue) v).getInt(), null) :
GameMode.byName(v.getString().toLowerCase(Locale.ROOT), null);
if (toSet != null) ((ServerPlayerEntity) e).setGameMode(toSet);
});
put("jumping",(e,v)->{
if(!(e instanceof LivingEntity)) return;
((LivingEntity) e).setJumping(v.getBoolean());
});
put("jump",(e,v)->{
if (e instanceof LivingEntity)
{
((LivingEntityInterface)e).doJumpCM();
}
else
{
genericJump(e);
}
});
put("silent",(e,v)-> e.setSilent(v.getBoolean()));
put("gravity",(e,v)-> e.setNoGravity(!v.getBoolean()));
put("invulnerable",(e,v)-> e.setInvulnerable(v.getBoolean()));
put("fire",(e,v)-> e.setFireTicks((int)NumericValue.asNumber(v).getLong()));
put("hunger", (e, v)-> {
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setFoodLevel((int) NumericValue.asNumber(v).getLong());
});
put("exhaustion", (e, v)-> {
if(e instanceof PlayerEntity) ((HungerManagerInterface) ((PlayerEntity) e).getHungerManager()).setExhaustionCM(NumericValue.asNumber(v).getFloat());
});
put("add_exhaustion", (e, v)-> {
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong());
});
put("saturation", (e, v)-> {
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong());
});
// gamemode [check]
// spectate [check]
// "fire" [check]
// "extinguish" [set fire ticks to 0]
// "silent" [check]
// "gravity" [check]
// "invulnerable" [check]
// "dimension" []
// "item" []
// "count", []
// "age", [check]
// "effect_"name []
// "hold" [inventory_set?]
// "hold_offhand" [inventory_set?]
// "jump" [check]
// "nbt" <-big one, for now use run('data merge entity ...
}};
@@ -0,0 +1,40 @@
package carpet.script.value;
import net.minecraft.text.BaseText;
public class FormattedTextValue extends StringValue
{
BaseText text;
public FormattedTextValue(BaseText text)
{
super(null);
this.text = text;
}
@Override
public String getString() {
return text.getString();
}
@Override
public boolean getBoolean() {
return text.getSiblings().size() > 0;
}
@Override
public Value clone()
{
return new FormattedTextValue(text);
}
@Override
public String getTypeString()
{
return "text";
}
public BaseText getText()
{
return text;
}
}
@@ -5,6 +5,7 @@ import carpet.script.Expression;
import carpet.script.Fluff;
import carpet.script.LazyValue;
import carpet.script.Tokenizer;
import carpet.script.bundled.Module;
import carpet.script.exception.BreakStatement;
import carpet.script.exception.ContinueStatement;
import carpet.script.exception.ExitStatement;
@@ -58,6 +59,8 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
return name;
}
public Module getModule() {return expression.module;}
@Override
public String getPrettyString()
{
@@ -48,7 +48,7 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
@Override
public String getString()
{
return String.format(Locale.ROOT, "[%d, %d, ..., %d]",start, start+stepp, limit);
return String.format(Locale.ROOT, "[%d, %d, ..., %d)",start, start+stepp, limit);
}
};
}
@@ -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<Value> list)
{
@@ -7,6 +7,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class MapValue extends AbstractListValue implements ContainerValueInterface
@@ -27,6 +28,15 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
}
}
public MapValue(Set<Value> keySet)
{
this();
for (Value v : keySet)
{
map.put(v, Value.NULL);
}
}
@Override
public Iterator<Value> iterator()
{
@@ -27,6 +27,7 @@ import net.minecraft.nbt.StringNbtReader;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
@@ -35,6 +36,7 @@ import net.minecraft.util.math.Box;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Matcher;
@@ -201,16 +203,30 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
return null;
return new InventoryLocator(pos, pos, inv, offset + 1);
}
else if (v1.getString().equalsIgnoreCase("enderchest"))
else if (v1 instanceof StringValue)
{
Value v2 = params.get(1 + offset).evalValue(c);
if (!(v2 instanceof EntityValue) || !(((EntityValue) v2).getEntity() instanceof PlayerEntity))
String strVal = v1.getString().toLowerCase(Locale.ROOT);
if (strVal.equals("enderchest"))
{
throw new InternalExpressionException("enderchest inventory requires player argument");
Value v2 = params.get(1 + offset).evalValue(c);
ServerPlayerEntity player = EntityValue.getPlayerByValue(c.s.getMinecraftServer(), v2);
if (player == null) throw new InternalExpressionException("enderchest inventory requires player argument");
return new InventoryLocator(player, player.getBlockPos(), player.getEnderChestInventory(), offset + 2, true);
}
else
{
boolean isEnder = strVal.startsWith("enderchest_");
if (isEnder) strVal = strVal.substring(11); // len("enderchest_")
ServerPlayerEntity player = c.s.getMinecraftServer().getPlayerManager().getPlayer(strVal);
if (player == null) throw new InternalExpressionException("String description of an inventory should either denote a player or player's enderchest");
return new InventoryLocator(
player,
player.getBlockPos(),
isEnder ? player.getEnderChestInventory() : player.inventory,
offset + 1,
isEnder
);
}
PlayerEntity e = (PlayerEntity)((EntityValue) v2).getEntity();
inv = e.getEnderChestInventory();
return new InventoryLocator(e, e.getBlockPos(), inv, offset + 2, true);
}
BlockPos pos = new BlockPos(
NumericValue.asNumber(v1).getDouble(),
@@ -13,6 +13,13 @@ public class NumericValue extends Value
private Double value;
final static double epsilon = 1024*Double.MIN_VALUE;
public static NumericValue asNumber(Value v1, String id)
{
if (!(v1 instanceof NumericValue))
throw new InternalExpressionException("Argument "+id+" has to be of a numeric type");
return ((NumericValue) v1);
}
public static NumericValue asNumber(Value v1)
{
if (!(v1 instanceof NumericValue))
@@ -56,6 +63,10 @@ public class NumericValue extends Value
{
return value;
}
public float getFloat()
{
return value.floatValue();
}
public static long floor(double double_1) {
long int_1 = (long)double_1;
@@ -1,6 +1,7 @@
package carpet.settings;
import carpet.CarpetServer;
import carpet.utils.Translations;
import carpet.utils.Messenger;
import com.google.common.collect.ImmutableList;
import net.minecraft.server.command.ServerCommandSource;
@@ -12,6 +13,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import static carpet.utils.Translations.tr;
public final class ParsedRule<T> implements Comparable<ParsedRule> {
public final Field field;
public final String name;
@@ -225,4 +228,33 @@ public final class ParsedRule<T> implements Comparable<ParsedRule> {
{
return this.name + ": " + getAsString();
}
private String translationKey()
{
return String.format("rule.%s.name", name);
}
public String translatedName(){
String key = translationKey();
return Translations.hasTranslation(key) ? tr(key) + String.format(" (%s)", name): name;
}
public String translatedDescription()
{
return tr(String.format("rule.%s.desc", (name)), description);
}
public List<String> translatedExtras()
{
if (!Translations.hasTranslations()) return extraInfo;
String keyBase = String.format("rule.%s.extra.", name);
List<String> extras = new ArrayList<>();
int i = 0;
while (Translations.hasTranslation(keyBase+i))
{
extras.add(Translations.tr(keyBase+i));
i++;
}
return (extras.isEmpty()) ? extraInfo : extras;
}
}
@@ -2,6 +2,8 @@ package carpet.settings;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.network.ServerNetworkHandler;
import carpet.utils.Translations;
import carpet.utils.Messenger;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
@@ -39,6 +41,7 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import static carpet.utils.Translations.tr;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
import static net.minecraft.server.command.CommandSource.suggestMatching;
@@ -78,7 +81,7 @@ public class SettingsManager
public void detachServer()
{
for (ParsedRule<?> rule : rules.values()) rule.resetToDefault(server.getCommandSource());
for (ParsedRule<?> rule : rules.values()) rule.resetToDefault(null);
server = null;
}
@@ -101,6 +104,7 @@ public class SettingsManager
void notifyRuleChanged(ServerCommandSource source, ParsedRule<?> rule, String userTypedValue)
{
observers.forEach(observer -> observer.accept(source, rule, userTypedValue));
ServerNetworkHandler.updateRuleWithConnectedClients(rule);
}
public Iterable<String> getCategories()
@@ -350,7 +354,7 @@ public class SettingsManager
String ruleName = StringArgumentType.getString(ctx, "rule");
ParsedRule<?> rule = getRule(ruleName);
if (rule == null)
throw new SimpleCommandExceptionType(Messenger.c("rb Unknown rule: "+ruleName)).create();
throw new SimpleCommandExceptionType(Messenger.c("rb "+ tr("ui.unknown_rule","Unknown rule")+": "+ruleName)).create();
return rule;
}
@@ -358,26 +362,25 @@ public class SettingsManager
{
if (dispatcher.getRoot().getChildren().stream().anyMatch(node -> node.getName().equalsIgnoreCase(identifier)))
{
Messenger.print_server_message(CarpetServer.minecraft_server,
Messenger.c("rb Failed to add settings command for " + identifier + ". It is masking previous command."));
CarpetSettings.LOG.error("Failed to add settings command for " + identifier + ". It is masking previous command.");
return;
}
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal(identifier).requires((player) ->
player.hasPermissionLevel(2) && !locked);
literalargumentbuilder.executes((context)->listAllSettings(context.getSource())).
literalargumentbuilder.executes((context)-> listAllSettings(context.getSource())).
then(literal("list").
executes( (c) -> listSettings(c.getSource(), "All "+fancyName+" Settings",
executes( (c) -> listSettings(c.getSource(), String.format(tr("ui.all_%(mod)s_settings","All %s Settings"), fancyName),
getRules())).
then(literal("defaults").
executes( (c)-> listSettings(c.getSource(),
"Current "+fancyName+" Startup Settings from "+identifier+".conf",
String.format(tr("ui.current_%(mod)s_startup_settings_from_%(conf)s","Current %s Startup Settings from %s"), fancyName, (identifier+".conf")),
findStartupOverrides()))).
then(argument("tag",StringArgumentType.word()).
suggests( (c, b)->suggestMatching(getCategories(), b)).
executes( (c) -> listSettings(c.getSource(),
String.format(fancyName+" Settings matching \"%s\"", StringArgumentType.getString(c, "tag")),
String.format(tr("ui.%(mod)s_settings_matching_'%(query)s'","%s Settings matching \"%s\""), fancyName, tr("category." + StringArgumentType.getString(c, "tag"),StringArgumentType.getString(c, "tag"))),
getRulesMatching(StringArgumentType.getString(c, "tag")))))).
then(literal("removeDefault").
requires(s -> !locked).
@@ -405,33 +408,35 @@ public class SettingsManager
private int displayRuleMenu(ServerCommandSource source, ParsedRule<?> rule)
{
PlayerEntity player;
String displayName = rule.translatedName();
try
{
player = source.getPlayer();
}
catch (CommandSyntaxException e)
{
Messenger.m(source, "w "+rule.name +" is set to: ","wb "+rule.getAsString());
Messenger.m(source, "w "+ displayName +" "+ tr( "ui.is_set_to","is set to")+": ","wb "+rule.getAsString());
return 1;
}
Messenger.m(player, "");
Messenger.m(player, "wb "+rule.name,"!/"+identifier+" "+rule.name,"^g refresh");
Messenger.m(player, "w "+rule.description);
Messenger.m(player, "wb "+ displayName ,"!/"+identifier+" "+rule.name,"^g refresh");
Messenger.m(player, "w "+ rule.translatedDescription());
rule.extraInfo.forEach(s -> Messenger.m(player, "g "+s));
rule.translatedExtras().forEach(s -> Messenger.m(player, "g "+s));
List<BaseText> tags = new ArrayList<>();
tags.add(Messenger.c("w Tags: "));
tags.add(Messenger.c("w "+ tr("ui.tags", "Tags")+": "));
for (String t: rule.categories)
{
tags.add(Messenger.c("c ["+t+"]", "^g list all "+t+" settings","!/"+identifier+" list "+t));
String translated = tr("category." + t, t);
tags.add(Messenger.c("c ["+ translated +"]", "^g "+ String.format(tr("list_all_%s_settings","list all %s settings"), translated),"!/"+identifier+" list "+t));
tags.add(Messenger.c("w , "));
}
tags.remove(tags.size()-1);
Messenger.m(player, tags.toArray(new Object[0]));
Messenger.m(player, "w Current value: ",String.format("%s %s (%s value)",rule.getBoolValue()?"lb":"nb", rule.getAsString(),rule.isDefault()?"default":"modified"));
Messenger.m(player, "w "+ tr("ui.current_value", "Current value")+": ",String.format("%s %s (%s value)",rule.getBoolValue()?"lb":"nb", rule.getAsString(),rule.isDefault()?"default":"modified"));
List<BaseText> options = new ArrayList<>();
options.add(Messenger.c("w Options: ", "y [ "));
for (String o: rule.options)
@@ -449,8 +454,8 @@ public class SettingsManager
private int setRule(ServerCommandSource source, ParsedRule<?> rule, String newValue)
{
if (rule.set(source, newValue) != null)
Messenger.m(source, "w "+rule.toString()+", ", "c [change permanently?]",
"^w Click to keep the settings in "+identifier+".conf to save across restarts",
Messenger.m(source, "w "+rule.toString()+", ", "c ["+ tr("ui.change_permanently","change permanently")+"?]",
"^w "+String.format(tr("ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts","Click to keep the settings in %s to save across restarts"), identifier+".conf"),
"?/"+identifier+" setDefault "+rule.name+" "+rule.getAsString());
return 1;
}
@@ -465,7 +470,7 @@ public class SettingsManager
writeSettingsToConf(conf.getLeft()); // this may feels weird, but if conf
// is locked, it will never reach this point.
rule.set(source,stringValue);
Messenger.m(source ,"gi rule "+ rule.name+" will now default to "+ stringValue);
Messenger.m(source ,"gi "+String.format(tr("ui.rule_%(rule)s_will_now_default_to_%(value)s","rule %s will now default to %s"), rule.translatedName(), stringValue));
return 1;
}
// removes overrides of the default values in the file
@@ -482,10 +487,11 @@ public class SettingsManager
private BaseText displayInteractiveSetting(ParsedRule<?> rule)
{
String displayName = rule.translatedName();
List<Object> args = new ArrayList<>();
args.add("w - "+rule.name+" ");
args.add("w - "+ displayName +" ");
args.add("!/"+identifier+" "+rule.name);
args.add("^y "+rule.description);
args.add("^y "+rule.translatedDescription());
for (String option: rule.options)
{
args.add(makeSetRuleButton(rule, option, true));
@@ -511,10 +517,10 @@ public class SettingsManager
}
String baseText = style + (brackets ? " [" : " ") + option + (brackets ? "]" : "");
if (locked)
return Messenger.c(baseText, "^g "+fancyName+" settings are locked");
return Messenger.c(baseText, "^g "+fancyName+" " + tr("ui.settings_are_locked","settings are locked"));
if (option.equalsIgnoreCase(rule.getAsString()))
return Messenger.c(baseText);
return Messenger.c(baseText, "^g Switch to " + option+(option.equalsIgnoreCase(rule.defaultAsString)?" (default)":""), "?/"+identifier+" " + rule.name + " " + option);
return Messenger.c(baseText, "^g "+ tr("ui.switch_to","Switch to") +" " + option+(option.equalsIgnoreCase(rule.defaultAsString)?" (default)":""), "?/"+identifier+" " + rule.name + " " + option);
}
private int listSettings(ServerCommandSource source, String title, Collection<ParsedRule<?>> settings_list)
@@ -535,19 +541,26 @@ public class SettingsManager
}
private int listAllSettings(ServerCommandSource source)
{
listSettings(source, "Current "+fancyName+" Settings", getNonDefault());
//listSettings(source, "Current "+fancyName+" Settings", getNonDefault());
listSettings(source, String.format(tr("ui.current_%(mod)s_settings","Current %s Settings"), fancyName), getNonDefault());
if (version != null)
Messenger.m(source, "g "+fancyName+" version: "+ version);
//Messenger.m(source, "g "+fancyName+" version: "+ version);
Messenger.m(source, "g "+fancyName+" "+ tr("ui.version", "version") + ": "+ version);
try
{
PlayerEntity player = source.getPlayer();
List<Object> tags = new ArrayList<>();
tags.add("w Browse Categories:\n");
//tags.add("w Browse Categories:\n");
tags.add("w " + tr("ui.browse_categories", "Browse Categories") + ":\n");
for (String t : getCategories())
{
tags.add("c [" + t+"]");
tags.add("^g list all " + t + " settings");
String catKey = "category." + t;
String translated = tr(catKey, t);
String translatedPlus = Translations.hasTranslation(catKey) ? String.format("%s (%s)",tr(catKey, t), t) : t;
tags.add("c [" + translated +"]");
//tags.add("^g list all " + translated + " settings");
tags.add("^g " + String.format(tr("ui.list_all_%(cat)s_settings","list all %s settings"), translatedPlus));
tags.add("!/"+identifier+" list " + t);
tags.add("w ");
}
@@ -558,7 +571,7 @@ public class SettingsManager
return 1;
}
public void inspectClientsideCommand(String string)
public void inspectClientsideCommand(ServerCommandSource source, String string)
{
if (string.startsWith("/carpet "))
{
@@ -569,11 +582,9 @@ public class SettingsManager
String strOption = res[2];
if (rules.containsKey(setting) && rules.get(setting).isClient)
{
rules.get(setting).set(null, strOption);
rules.get(setting).set(source, strOption);
}
}
}
}
}
+12 -12
View File
@@ -17,23 +17,12 @@ public abstract class Validator<T>
public abstract T validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string);
public String description() {return null;}
public static class POSITIVE_NUMBER<T extends Number> extends Validator<T>
{
@Override
public T validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string)
{
return newValue.doubleValue() > 0 ? newValue : null;
}
@Override
public String description() { return "Must be a positive number"; }
}
public static class _COMMAND<T> extends Validator<T>
{
@Override
public T validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string)
{
if (CarpetServer.settingsManager != null)
if (CarpetServer.settingsManager != null && source != null)
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
return newValue;
}
@@ -119,4 +108,15 @@ public abstract class Validator<T>
@Override
public String description() { return "Must be a positive number";}
}
public static class PROBABILITY <T extends Number> extends Validator<T>
{
@Override
public T validate(ServerCommandSource source, ParsedRule<T> currentRule, T newValue, String string)
{
return (newValue.doubleValue() >= 0 && newValue.doubleValue() <= 1 )? newValue : null;
}
@Override
public String description() { return "Must be between 0 and 1";}
}
}
+11 -11
View File
@@ -109,21 +109,21 @@ public class BlockInfo
put(Material.CARPET , "carpet" );
put(Material.PLANT , "plant" );
put(Material.UNDERWATER_PLANT, "water_plant" );
put(Material.REPLACEABLE_PLANT, "vine" );
put(Material.SEAGRASS , "sea_grass" );
put(Material.REPLACEABLE_PLANT, "vegetation" );
put(Material.REPLACEABLE_UNDERWATER_PLANT , "sea_grass" );
put(Material.WATER , "water" );
put(Material.BUBBLE_COLUMN , "bubble_column");
put(Material.LAVA , "lava" );
put(Material.SNOW , "snow_layer" );
put(Material.SNOW_LAYER , "snow_layer" );
put(Material.FIRE , "fire" );
put(Material.PART , "redstone_bits");
put(Material.SUPPORTED , "decoration" );
put(Material.COBWEB , "cobweb" );
put(Material.REDSTONE_LAMP , "redstone_lamp");
put(Material.CLAY , "clay" );
put(Material.EARTH , "dirt" );
put(Material.ORGANIC , "grass" );
put(Material.PACKED_ICE , "packed_ice" );
put(Material.SAND , "sand" );
put(Material.ORGANIC_PRODUCT , "clay" );
put(Material.SOIL , "dirt" );
put(Material.SOLID_ORGANIC , "grass" );
put(Material.DENSE_ICE , "packed_ice" );
put(Material.AGGREGATE , "sand" );
put(Material.SPONGE , "sponge" );
put(Material.SHULKER_BOX , "shulker" );
put(Material.WOOD , "wood" );
@@ -138,11 +138,11 @@ public class BlockInfo
put(Material.STONE , "stone" );
put(Material.METAL , "metal" );
put(Material.SNOW_BLOCK , "snow" );
put(Material.ANVIL , "anvil" );
put(Material.REPAIR_STATION , "anvil" );
put(Material.BARRIER , "barrier" );
put(Material.PISTON , "piston" );
put(Material.UNUSED_PLANT , "coral" );
put(Material.PUMPKIN , "gourd" );
put(Material.GOURD , "gourd" );
put(Material.EGG , "dragon_egg" );
put(Material.CAKE , "cake" );
}};
+43 -35
View File
@@ -3,14 +3,15 @@ package carpet.utils;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.EntityCategory;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.text.LiteralText;
import net.minecraft.util.Formatting;
import net.minecraft.text.BaseText;
import net.minecraft.server.MinecraftServer;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Style;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.Formatting;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.dimension.DimensionType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -52,32 +53,33 @@ public class Messenger
/ = action added to the previous component
*/
private static BaseText _applyStyleToTextComponent(BaseText comp, String style)
public static Style parseStyle(String style)
{
//could be rewritten to be more efficient
comp.getStyle().setItalic(style.indexOf('i')>=0);
comp.getStyle().setStrikethrough(style.indexOf('s')>=0);
comp.getStyle().setUnderline(style.indexOf('u')>=0);
comp.getStyle().setBold(style.indexOf('b')>=0);
comp.getStyle().setObfuscated(style.indexOf('o')>=0);
comp.getStyle().setColor(Formatting.WHITE);
if (style.indexOf('w')>=0) comp.getStyle().setColor(Formatting.WHITE); // not needed
if (style.indexOf('y')>=0) comp.getStyle().setColor(Formatting.YELLOW);
if (style.indexOf('m')>=0) comp.getStyle().setColor(Formatting.LIGHT_PURPLE);
if (style.indexOf('r')>=0) comp.getStyle().setColor(Formatting.RED);
if (style.indexOf('c')>=0) comp.getStyle().setColor(Formatting.AQUA);
if (style.indexOf('l')>=0) comp.getStyle().setColor(Formatting.GREEN);
if (style.indexOf('t')>=0) comp.getStyle().setColor(Formatting.BLUE);
if (style.indexOf('f')>=0) comp.getStyle().setColor(Formatting.DARK_GRAY);
if (style.indexOf('g')>=0) comp.getStyle().setColor(Formatting.GRAY);
if (style.indexOf('d')>=0) comp.getStyle().setColor(Formatting.GOLD);
if (style.indexOf('p')>=0) comp.getStyle().setColor(Formatting.DARK_PURPLE);
if (style.indexOf('n')>=0) comp.getStyle().setColor(Formatting.DARK_RED);
if (style.indexOf('q')>=0) comp.getStyle().setColor(Formatting.DARK_AQUA);
if (style.indexOf('e')>=0) comp.getStyle().setColor(Formatting.DARK_GREEN);
if (style.indexOf('v')>=0) comp.getStyle().setColor(Formatting.DARK_BLUE);
if (style.indexOf('k')>=0) comp.getStyle().setColor(Formatting.BLACK);
return comp;
Style styleInstance = new Style();
styleInstance.setItalic(style.indexOf('i')>=0);
styleInstance.setStrikethrough(style.indexOf('s')>=0);
styleInstance.setUnderline(style.indexOf('u')>=0);
styleInstance.setBold(style.indexOf('b')>=0);
styleInstance.setObfuscated(style.indexOf('o')>=0);
styleInstance.setColor(Formatting.WHITE);
if (style.indexOf('w')>=0) styleInstance.setColor(Formatting.WHITE); // not needed
if (style.indexOf('y')>=0) styleInstance.setColor(Formatting.YELLOW);
if (style.indexOf('m')>=0) styleInstance.setColor(Formatting.LIGHT_PURPLE);
if (style.indexOf('r')>=0) styleInstance.setColor(Formatting.RED);
if (style.indexOf('c')>=0) styleInstance.setColor(Formatting.AQUA);
if (style.indexOf('l')>=0) styleInstance.setColor(Formatting.GREEN);
if (style.indexOf('t')>=0) styleInstance.setColor(Formatting.BLUE);
if (style.indexOf('f')>=0) styleInstance.setColor(Formatting.DARK_GRAY);
if (style.indexOf('g')>=0) styleInstance.setColor(Formatting.GRAY);
if (style.indexOf('d')>=0) styleInstance.setColor(Formatting.GOLD);
if (style.indexOf('p')>=0) styleInstance.setColor(Formatting.DARK_PURPLE);
if (style.indexOf('n')>=0) styleInstance.setColor(Formatting.DARK_RED);
if (style.indexOf('q')>=0) styleInstance.setColor(Formatting.DARK_AQUA);
if (style.indexOf('e')>=0) styleInstance.setColor(Formatting.DARK_GREEN);
if (style.indexOf('v')>=0) styleInstance.setColor(Formatting.DARK_BLUE);
if (style.indexOf('k')>=0) styleInstance.setColor(Formatting.BLACK);
return styleInstance;
}
public static String heatmap_color(double actual, double reference)
{
@@ -114,10 +116,14 @@ public class Messenger
{
message = "w"+message;
}
String[] parts = message.split("\\s", 2);
String desc = parts[0];
int limit = message.indexOf(' ');
String desc = message;
String str = "";
if (parts.length > 1) str = parts[1];
if (limit >= 0)
{
desc = message.substring(0, limit);
str = message.substring(limit+1);
}
if (desc.charAt(0) == '/') // deprecated
{
if (previous_message != null)
@@ -143,7 +149,8 @@ public class Messenger
return previous_message;
}
BaseText txt = new LiteralText(str);
return _applyStyleToTextComponent(txt, desc);
txt.setStyle(parseStyle(desc));
return txt;
}
public static BaseText tp(String desc, Vec3d pos) { return tp(desc, pos.x, pos.y, pos.z); }
public static BaseText tp(String desc, BlockPos pos) { return tp(desc, pos.getX(), pos.getY(), pos.getZ()); }
@@ -224,7 +231,8 @@ public class Messenger
//message source
public static void m(ServerCommandSource source, Object ... fields)
{
source.sendFeedback(Messenger.c(fields),source.getMinecraftServer().getWorld(DimensionType.OVERWORLD) != null);
if (source != null)
source.sendFeedback(Messenger.c(fields),source.getMinecraftServer() != null && source.getMinecraftServer().getWorld(DimensionType.OVERWORLD) != null);
}
public static void m(PlayerEntity player, Object ... fields)
{
@@ -263,7 +271,7 @@ public class Messenger
public static BaseText s(String text, String style)
{
BaseText message = new LiteralText(text);
_applyStyleToTextComponent(message, style);
message.setStyle(parseStyle(style));
return message;
}
+27
View File
@@ -4,7 +4,13 @@ import carpet.CarpetServer;
import com.google.common.collect.Sets;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.tag.FluidTags;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import java.util.HashMap;
@@ -93,4 +99,25 @@ public class MobAI
}
}
/**
* Not a replacement for living entity jump() - this barely is to allow other entities that can't jump in vanilla to 'jump'
* @param e
*/
public static void genericJump(Entity e)
{
if (!e.onGround && !e.isInFluid(FluidTags.WATER) && !e.isInLava()) return;
float m = e.world.getBlockState(new BlockPos(e)).getBlock().getJumpVelocityMultiplier();
float g = e.world.getBlockState(new BlockPos(e.getX(), e.getBoundingBox().y1 - 0.5000001D, e.getZ())).getBlock().getJumpVelocityMultiplier();
float jumpVelocityMultiplier = (double) m == 1.0D ? g : m;
float jumpStrength = (0.42F * jumpVelocityMultiplier);
Vec3d vec3d = e.getVelocity();
e.setVelocity(vec3d.x, jumpStrength, vec3d.z);
if (e.isSprinting())
{
float u = e.yaw * 0.017453292F;
e.setVelocity(e.getVelocity().add((-MathHelper.sin(g) * 0.2F), 0.0D, (MathHelper.cos(u) * 0.2F)));
}
e.velocityDirty = true;
}
}
@@ -0,0 +1,16 @@
package carpet.utils;
import carpet.fakes.ChunkTicketManagerInterface;
import carpet.fakes.ServerChunkManagerInterface;
import net.minecraft.server.world.ChunkTicketManager;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.util.math.ChunkPos;
public class SpawnChunks
{
public static void changeSpawnChunks(ServerChunkManager chunkManager, ChunkPos pos, int size)
{
ChunkTicketManager ticketManager = ((ServerChunkManagerInterface)chunkManager).getCMTicketManager();
((ChunkTicketManagerInterface)ticketManager).changeSpawnChunks(pos, size);
}
}
+14 -12
View File
@@ -186,7 +186,7 @@ public class SpawnReporter
}
else
{
return printEntitiesByType(creature_type, worldIn);
return printEntitiesByType(creature_type, worldIn, true);
}
@@ -250,22 +250,24 @@ public class SpawnReporter
return get_type_string(get_creature_type_from_code(str));
}
public static List<BaseText> printEntitiesByType(EntityCategory cat, World worldIn) //Class<?> entityType)
public static List<BaseText> printEntitiesByType(EntityCategory cat, World worldIn, boolean all) //Class<?> entityType)
{
List<BaseText> lst = new ArrayList<>();
lst.add( Messenger.s(String.format("Loaded entities for %s class:", get_type_string(cat))));
for (Entity entity : ((ServerWorld)worldIn).getEntities(null, (e) -> e.getType().getCategory()==cat))
{
if (!(entity instanceof MobEntity) || !((MobEntity)entity).isPersistent())
{
EntityType type = entity.getType();
BlockPos pos = entity.getBlockPos();
lst.add( Messenger.c(
"w - ",
Messenger.tp("wb",pos),
String.format("w : %s", type.getName().getString())
));
}
boolean persistent = entity instanceof MobEntity && ( ((MobEntity) entity).isPersistent() || ((MobEntity) entity).cannotDespawn());
if (!all && persistent)
continue;
EntityType type = entity.getType();
BlockPos pos = entity.getBlockPos();
lst.add( Messenger.c(
"w - ",
Messenger.tp(persistent?"gb":"wb",pos),
String.format(persistent?"g : %s":"w : %s", type.getName().getString())
));
}
if (lst.size()==1)
{
@@ -0,0 +1,94 @@
package carpet.utils;
import carpet.CarpetExtension;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import net.minecraft.server.command.ServerCommandSource;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class Translations
{
private static Map<String, String> translationMap;
public static String tr(String key)
{
return translationMap == null ? key : translationMap.getOrDefault(key, key);
}
public static String tr(String key, String str)
{
return translationMap == null ? str : translationMap.getOrDefault(key, str);
}
public static boolean hasTranslations()
{
return translationMap != null;
}
public static boolean hasTranslation(String key)
{
return translationMap != null && translationMap.containsKey(key);
}
public static Map<String, String> getTranslationFromResourcePath(String path)
{
String dataJSON;
try
{
dataJSON = IOUtils.toString(
Objects.requireNonNull(Translations.class.getClassLoader().getResourceAsStream(String.format("assets/carpet/lang/%s.json", CarpetSettings.language))),
StandardCharsets.UTF_8);
} catch (NullPointerException | IOException e) {
return null;
}
Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
return gson.fromJson(dataJSON, new TypeToken<Map<String, String>>() {}.getType());
}
public static void updateLanguage(ServerCommandSource source)
{
if (CarpetSettings.language.equalsIgnoreCase("none"))
{
translationMap = null;
return;
}
Map<String, String> translations = new HashMap<>();
Map<String, String> trans = getTranslationFromResourcePath(String.format("assets/carpet/lang/%s.json", CarpetSettings.language));
if (trans != null) trans.forEach(translations::put);
for (CarpetExtension ext : CarpetServer.extensions)
{
Map<String, String> extMappings = ext.canHasTranslations(CarpetSettings.language);
if (extMappings != null)
{
extMappings.forEach((key, value) ->
{
if (!translations.containsKey(key)) translations.put(key, value);
});
}
}
translations.entrySet().removeIf(e -> e.getKey().startsWith("//"));
if (translations.isEmpty())
{
translationMap = null;
return;
}
translationMap = translations;
}
public static boolean isValidLanguage(String newValue)
{
// will put some validations for availble languages at some point
return true;
}
}
@@ -0,0 +1,53 @@
// This file isn't necessary at all, because the language module would use the strings coded in the source if language "none" is selected.
// So en_us is the default language and probably no change will be made after loading this file.
// But it could be a good example for writing the language file for other languages.
{
"// Rules:": "",
"rule.portalSuffocationFix.name": "",
"// the key is genetated as:" : "String.format('rule.%s.name', rule.name)",
"rule.portalSuffocationFix.desc": "",
"// the key is genetated as:" : "String.format('rule.%s.desc', rule.name)",
"rule.portalSuffocationFix.extra.0": "",
"rule.portalSuffocationFix.extra.1": "",
"// the key is genetated as:" : "String.format('rule.%s.extra.%i', rule.name, index)",
"// Categories:": "",
"category.bugfix": "bugfix",
"category.survival": "survival",
"category.creative": "creative",
"category.experimental": "experimental",
"category.optimization": "optimization",
"category.feature": "feature",
"category.command": "command",
"category.tnt": "TNT",
"category.dispenser": "dispenser",
"category.scarpet": "scarpet",
"category.client": "client",
"// Settings command UI elements": "",
"ui.browse_categories": "Browse categories",
"ui.list_all_%(cat)s_settings": "list all %s settings",
"ui.version": "Version",
"ui.current_%(mod)s_settings": "Current %s settings",
"ui.switch_to": "Switch to",
"ui.settings_are_locked": "settings are locked",
"ui.is_set_to": "is set to",
"ui.unknown_rule": "Unknown rule",
"ui.current_%(mod)s_startup_settings_from_%(conf)s": "Current %s Startup Settings from %s",
"ui.%(mod)s_settings_matching_'%(query)s'": "%s settings matching \"%s\" ",
"ui.all_%(mod)s_settings": "All %s settings",
"ui.tags": "Tags",
"ui.change_permanently": "Change permanently",
"ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts": "click to keep the settings in %s to save across restarts",
"ui.rule_%(rule)s_will_now_default_to_%(value)s": "rule %s will now default to %s",
"ui.current_value": "current value"
}
@@ -0,0 +1,234 @@
{
"rule.antiCheatDisabled.name": "移动检测通杀",
"rule.antiCheatDisabled.desc": "防止玩家因为“本服务器未启用飞行”,“移动过快”等原因被踢出",
"rule.carpets.name": "放置地毯执行功能",
"rule.carpets.desc": "通过放置地毯方块快捷执行部分功能",
"rule.combineXPOrbs.name": "经验球合并",
"rule.combineXPOrbs.desc": "会将多个小经验球合并为更大的经验球",
"rule.commandCameramode.name": "灵魂出窍模式",
"rule.commandCameramode.desc": "启用/c和/s命令使得玩家可以在生存模式和观察者模式间切换",
"rule.commandCameramode.extra.0": "/c和/s命令所有玩家(包括非OP玩家)都可以使用",
"rule.commandDistance.name": "距离测量",
"rule.commandDistance.desc": "启用/distance命令以测量游戏中两点间距离",
"rule.commandDistance.extra.0": "可以通过放置棕色地毯的方式使用本功能,前提是“放置地毯执行功能”已启用",
"rule.commandDraw.name": "画图",
"rule.commandDraw.desc": "启用/draw命令以允许绘制一些简单图形",
"rule.commandInfo.name": "获取方块数据",
"rule.commandInfo.desc": "启用/info命令以获取方块数据",
"rule.commandInfo.extra.0": "可以通过放置灰色地毯的方式使用本功能,前提是“放置地毯执行功能”已启用",
"rule.commandLog.name": "游戏数据监视器",
"rule.commandLog.desc": "启用/log命令以在聊天栏/Tab栏监视游戏部分数据",
"rule.commandPerimeterInfo.name": "刷怪区域搜寻器",
"rule.commandPerimeterInfo.desc": "启用/perimeterinfo命令以搜寻周围的可刷怪区域",
"rule.commandPlayer.name": "假人控制",
"rule.commandPlayer.desc": "启用/player命令以控制/召唤玩家",
"rule.commandScript.name": "地毯脚本控制器",
"rule.commandScript.desc": "启用/script相关命令",
"rule.commandScript.extra.0": "一个Scarpet语言的游戏内API",
"rule.commandSpawn.name": "刷怪追踪器",
"rule.commandSpawn.desc": "启用/spawn命令用于怪物生成追踪",
"rule.commandTick.name": "游戏时钟控制器",
"rule.commandTick.desc": "启用/tick命令以控制游戏时钟",
"rule.commandTrackAI.name": "AI追踪器",
"rule.commandTrackAI.desc": "启用/track命令以追踪生物的AI",
"rule.ctrlQCraftingFix.name": "ctrlQ合成修复",
"rule.ctrlQCraftingFix.desc": "在合成时允许使用ctrl+Q快捷键扔出结果格内所有物品",
"rule.customMOTD.name": "MOTD更改",
"rule.customMOTD.desc": "设置一个不同的MOTD信息",
"rule.customMOTD.extra.0": "使用'_'时将发送默认MOTD(从server.properties中读取)",
"rule.defaultLoggers.name": "默认游戏监视器",
"rule.defaultLoggers.desc": "为所有新进入玩家设置部分监视器默认开启",
"rule.defaultLoggers.extra.0": "比如设置'tps,mobcaps'作为默认监视器",
"rule.desertShrubs.name": "树苗在沙漠干枯",
"rule.desertShrubs.desc": "树苗将在附近无水源的沙漠群系枯萎",
"rule.disableSpawnChunks.name": "允许出生点区块卸载",
"rule.disableSpawnChunks.desc": "允许出生点区块卸载",
"rule.explosionNoBlockDamage.name": "防爆",
"rule.explosionNoBlockDamage.desc": "任何形式的爆炸都不会摧毁方块",
"rule.extremeBehaviours.name": "随机极端情况",
"rule.extremeBehaviours.desc": "大幅度提升极端情况出现概率,仅用于测试",
"rule.extremeBehaviours.extra.0": "发射器和投掷器将使用最大随机刻",
"rule.fastRedstoneDust.name": "红石粉卡顿优化",
"rule.fastRedstoneDust.desc": "红石粉卡顿优化 作者 Theosib",
"rule.fillLimit.name": "fill/clone上限更改",
"rule.fillLimit.desc": "将fill/clone上限更改为自己需要的值",
"rule.fillLimit.extra.0": "你必须在1-20M之间取一个值",
"rule.fillUpdates.name": "fill更新开关",
"rule.fillUpdates.desc": "fill/clone/setblock命令以及结构方块执行时是否产生更新",
"rule.fillUpdates.extra.0": "设置为true有更新 设置为false时为无更新",
"rule.flatWorldStructureSpawning.name": "结构怪在超平坦世界生成",
"rule.flatWorldStructureSpawning.desc": "允许结构怪在超平坦世界生成",
"rule.flippinCactus.name": "仙人掌扳手",
"rule.flippinCactus.desc": "允许使用仙人掌调整方块朝向,并且不会产生更新",
"rule.hardcodeTNTangle.name": "硬编码TNT角度",
"rule.hardcodeTNTangle.desc": "把TNT水平随机角度设为固定值,可用于测试机器",
"rule.hardcodeTNTangle.extra.0": "默认为-1,必须在0与360之间,或者-1",
"rule.hopperCounters.name": "漏斗计数器",
"rule.hopperCounters.desc": "漏斗指向的羊毛会计算经过的物品",
"rule.hopperCounters.extra.0": "在羊毛上放置红色或绿色地毯,使用/counter命令可启用",
"rule.hopperCounters.extra.1": "使用 /counter <color?> reset重置计数器,使用/counter <color?>查询",
"rule.hopperCounters.extra.2": "在生存模式,在同色羊毛上放绿地毯可以查询,放红地毯重置",
"rule.hopperCounters.extra.3": "数据是全局性在玩家间共享的,并且有16颜色可用",
"rule.hopperCounters.extra.4": "物品会被销毁,每个漏斗每tick销毁一组物品",
"rule.huskSpawningInTemples.name": "尸壳在神殿里生成",
"rule.huskSpawningInTemples.desc": "只有尸壳会在沙漠神殿生成",
"rule.lagFreeSpawning.name": "延迟自然生成",
"rule.lagFreeSpawning.desc": "生物生成需要更少的CPU和内存",
"rule.leadFix.name": "栓绳修复",
"rule.leadFix.desc": "修复栓绳在未加载区块中,变得不可见的问题",
"rule.maxEntityCollisions.name": "最大实体挤压数",
"rule.maxEntityCollisions.desc": "设定最大实体挤压限制,0表示无限大",
"rule.mergeTNT.name": "合并TNT",
"rule.mergeTNT.desc": "合并静止的点燃的tnt实体",
"rule.missingTools.name": "工具缺失修复",
"rule.missingTools.desc": "活塞,玻璃和海绵可以用合适的工具更快地破坏",
"rule.movableBlockEntities.name": "容器移动",
"rule.movableBlockEntities.desc": "活塞可推动方块实体,比如箱子,漏斗",
"rule.onePlayerSleeping.name": "单个玩家睡觉",
"rule.onePlayerSleeping.desc": "服务器里一个玩家睡觉可跳过夜晚",
"rule.optimizedTNT.name": "TNT优化",
"rule.optimizedTNT.desc": "tnt在相同的地点或在流体里,爆炸造成更小的延迟",
"rule.persistentParrots.name": "鹦鹉停留",
"rule.persistentParrots.desc": "鹦鹉不会离开你的肩膀,除非你收到伤害",
"rule.placementRotationFix.name": "放置旋转修复",
"rule.placementRotationFix.desc": "修复了玩家放置方块时快速转身造成的问题",
"rule.pushLimit.name": "自定义推动上限",
"rule.pushLimit.desc": "自定义活塞推动上限",
"rule.pushLimit.extra.0": "你必须在1-1024中选取一个值",
"rule.quasiConnectivity.name": "qC激活开关",
"rule.quasiConnectivity.desc": "活塞,发射器和投掷器在他们上方方块激活时是否响应",
"rule.quasiConnectivity.extra.0": "设置为true为开启 设置为false为禁用",
"rule.railPowerLimit.name": "自定义动力铁轨激活距离",
"rule.railPowerLimit.desc": "动力铁轨激活上限可自定义",
"rule.railPowerLimit.extra.0": "你必须在1-1024中选取一个值",
"rule.renewableCoral.name": "珊瑚可再生",
"rule.renewableCoral.desc": "使用骨粉可催熟珊瑚以生成珊瑚结构",
"rule.renewableSponges.name": "海绵可再生",
"rule.renewableSponges.desc": "守卫者将在被雷击后会变为远古守卫者",
"rule.rotatorBlock.name": "仙人掌扳手发射器版",
"rule.rotatorBlock.desc": "发射器中的仙人掌可以旋转方块(尽可能以逆时针)",
"rule.scriptsAutoload.name": "Scarpet脚本自动加载",
"rule.scriptsAutoload.desc": "在服务器/世界加载的时候将会自动从世界文件中加载Scarpet脚本文件",
"rule.scriptsAutoload.extra.0": "/script命令必须启用",
"rule.shulkerSpawningInEndCities.name": "潜影贝重生",
"rule.shulkerSpawningInEndCities.desc": "潜影贝将会在末地城中重生",
"rule.silverFishDropGravel.name": "蠹虫钻出方块掉落沙砾",
"rule.silverFishDropGravel.desc": "蠹虫钻出方块时会掉落沙砾",
"rule.smoothClientAnimations.name": "平滑客户端动画",
"rule.smoothClientAnimations.desc": "低TPS设置时可以使客户端动画更加平滑",
"rule.smoothClientAnimations.extra.0": "仅在单人游戏中生效,并且会降低客户端运行速度",
"rule.stackableShulkerBoxes.name": "潜影盒堆叠",
"rule.stackableShulkerBoxes.desc": "空潜影盒仍在地上时将自动堆叠,直至堆叠为1组",
"rule.stackableShulkerBoxes.extra.0": "在物品GUI中,请使用shift键+单击以移动整组潜影盒",
"rule.summonNaturalLightning.name": "召唤闪电自然化",
"rule.summonNaturalLightning.desc": "召唤的闪电将拥有所有自然生成闪电拥有的特性",
"rule.tntDoNotUpdate.name": "TNT放置时不产生更新",
"rule.tntDoNotUpdate.desc": "TNT在放置时不会产生任何方块更新(不会激活)",
"rule.tntPrimerMomentumRemoved.name": "移除TNT点燃时随机动量",
"rule.tntPrimerMomentumRemoved.desc": "TNT点燃时的随机动量将被移除",
"rule.tntRandomRange.name": "TNT爆炸范围设置",
"rule.tntRandomRange.desc": "设置TNT随机爆炸范围为一个固定的值,设为-1以禁用",
"rule.tntRandomRange.extra.0": "必须启用optimizedTNT功能",
"rule.tntRandomRange.extra.1": "不能为负值,除了-1",
"rule.unloadedEntityFix.name": "未加载实体修复",
"rule.unloadedEntityFix.desc": "进入或者被推入未加载区块的实体不会消失",
"rule.viewDistance.name": "自定义视距",
"rule.viewDistance.desc": "改变服务器视距,设为0以使用默认值",
"rule.viewDistance.extra.0": "你必须在0(使用服务器默认设置)-32取一个整数",
"rule.xpNoCooldown.name": "经验吸收无冷却",
"rule.xpNoCooldown.desc": "玩家吸收经验球时不会有冷却时间",
"rule.portalSuffocationFix.name": "传送门窒息修复",
"rule.portalSuffocationFix.desc": "使下界传送门正确地传送实体",
"rule.portalSuffocationFix.extra.0": "传送的实体将不再卡在黑曜石里面",
"rule.portalCreativeDelay.name": "创造玩家不被地狱门传送",
"rule.portalCreativeDelay.desc": "创造模式的玩家将不会立即被地狱门传送",
"category.bugfix": "BUG修复",
"category.survival": "生存",
"category.creative": "创造",
"category.experimental": "试验性",
"category.optimization": "优化",
"category.feature": "特性",
"category.command": "指令",
"category.tnt": "TNT",
"category.dispenser": "发射器",
"category.scarpet": "scarpet脚本语言",
"category.client": "客户端",
"ui.browse_categories": "浏览分类",
"ui.list_all_%(cat)s_settings": "列出所有 %s 选项",
"ui.version": "版本",
"ui.current_%(mod)s_settings": "目前启用的 %s 选项",
"ui.switch_to": "切换至",
"ui.settings_are_locked": "选项被锁定",
"ui.is_set_to": "被设置为",
"ui.unknown_rule": "未知的规则选项",
"ui.current_%(mod)s_startup_settings_from_%(conf)s": "目前启用的%s选项,从%s中读取得",
"ui.%(mod)s_settings_matching_'%(query)s'": "%s中符合\"%s\"的选项:",
"ui.all_%(mod)s_settings": "所有的%s选项",
"ui.tags": "分类",
"ui.change_permanently": "永久更改",
"ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts": "点击此按钮以使此选项保存于%s,重启后依旧生效",
"ui.rule_%(rule)s_will_now_default_to_%(value)s": "规则%s将会被默认设置为%s",
"ui.current_value": "当前的值"
}
@@ -0,0 +1,234 @@
{
"rule.antiCheatDisabled.name": "關閉伺服器作弊檢測",
"rule.antiCheatDisabled.desc": "防止玩家因為“本伺服器不允許飛行”,“移動速度過快”等原因被請出伺服器",
"rule.carpets.name": "放置地毯顯示進階數據",
"rule.carpets.desc": "通過把地毯放置在地上取得更進階信息",
"rule.combineXPOrbs.name": "合併小經驗球",
"rule.combineXPOrbs.desc": "會將大量的小經驗球合併成等效的大經驗球",
"rule.commandCameramode.name": "旁觀者模式",
"rule.commandCameramode.desc": "允許玩家使用/c切換成旁觀者模式,利用/s切換回生存模式",
"rule.commandCameramode.extra.0": "/c及/s不需要op權限也可使用",
"rule.commandDistance.name": "距離測定",
"rule.commandDistance.desc": "使用/distance指令測定兩點的距離",
"rule.commandDistance.extra.0": "可以使用放置棕色地毯來使用此功能,前提是“放置地毯顯示進階數據”已啟用",
"rule.commandDraw.name": "繪製圖形",
"rule.commandDraw.desc": "使用/draw指令以允許繪製一些簡易圖形",
"rule.commandInfo.name": "獲取方塊進階資訊",
"rule.commandInfo.desc": "允許使用/info指令以獲得方塊進階資訊",
"rule.commandInfo.extra.0": "可以使用放置灰色地毯來使用此功能,前提是“放置地毯顯示進階數據”已啟用",
"rule.commandLog.name": "遊戲數據追蹤器",
"rule.commandLog.desc": "允許使用/log指令使在Tab欄位追蹤遊戲部分數據",
"rule.commandPerimeterInfo.name": "刷怪區域追蹤器",
"rule.commandPerimeterInfo.desc": "允許使用/perimeterinfo指令以探查周圍可生怪區域",
"rule.commandPlayer.name": "機器人控制",
"rule.commandPlayer.desc": "允許使用/player指令以控制/召喚已存在的玩家id作為機器人",
"rule.commandScript.name": "地毯腳本控制器",
"rule.commandScript.desc": "允許使用/script相關指令",
"rule.commandScript.extra.0": "一個Scarpet程式語言的遊戲內API",
"rule.commandSpawn.name": "刷怪追蹤器",
"rule.commandSpawn.desc": "允許使用/spawn指令用於怪物生成追蹤",
"rule.commandTick.name": "遊戲時鐘控制器",
"rule.commandTick.desc": "允許使用/tick指令控制遊戲內的伺服器端運行速度",
"rule.commandTrackAI.name": "AI追蹤器",
"rule.commandTrackAI.desc": "允許使用/track指令以追蹤生物的AI",
"rule.ctrlQCraftingFix.name": "ctrlQ合成修復",
"rule.ctrlQCraftingFix.desc": "在合成時允許使用ctrl+Q快捷鍵扔出結果格內所有物品",
"rule.customMOTD.name": "MOTD更改",
"rule.customMOTD.desc": "設置一個不同的MOTD信息",
"rule.customMOTD.extra.0": "設置成'_'時將採用默認的MOTD(從server.properties中讀取的)",
"rule.defaultLoggers.name": "默認開啟的遊戲數據追蹤器",
"rule.defaultLoggers.desc": "為所有新加入的玩家設置部分遊戲數據追蹤器默認為開啟狀態",
"rule.defaultLoggers.extra.0": "比如設置'tps,mobcaps'最為默認遊戲數據追蹤",
"rule.desertShrubs.name": "樹苗在沙漠中乾枯",
"rule.desertShrubs.desc": "樹苗將在附近無水源狀態的沙漠生態群系變成枯灌木",
"rule.disableSpawnChunks.name": "允許出生點區塊被卸載",
"rule.disableSpawnChunks.desc": "允許出生點區塊被卸載",
"rule.explosionNoBlockDamage.name": "爆炸不破壞方塊",
"rule.explosionNoBlockDamage.desc": "任何形式的爆炸都無法摧毀方塊",
"rule.extremeBehaviours.name": "隨機參數極端化",
"rule.extremeBehaviours.desc": "大幅度提升極端情況出現的概率,僅用於測試",
"rule.extremeBehaviours.extra.0": "發射器和投擲器將使用最大隨機刻",
"rule.fastRedstoneDust.name": "紅石粉卡頓優化",
"rule.fastRedstoneDust.desc": "紅石粉卡頓優化 作者 Theosib",
"rule.fillLimit.name": "fill/clone上限更改",
"rule.fillLimit.desc": "將fill/clone上限更改為自己設定的值",
"rule.fillLimit.extra.0": "你必須在1-2千萬之間取一個整數值",
"rule.fillUpdates.name": "fill更新設置",
"rule.fillUpdates.desc": "fill/clone/setblock指令以及結構方塊執行時是否產生更新",
"rule.fillUpdates.extra.0": "設置為true時有更新 設置為false時為無更新",
"rule.flatWorldStructureSpawning.name": "特定建築怪在超平坦世界生成",
"rule.flatWorldStructureSpawning.desc": "允許特定建築的怪物在超平坦世界生成",
"rule.flippinCactus.name": "仙人掌扳手",
"rule.flippinCactus.desc": "允許使用仙人掌調整方塊朝向,並且不會造成更新",
"rule.hardcodeTNTangle.name": "強制TNT角度",
"rule.hardcodeTNTangle.desc": "把TNT水平隨機角度設定為固定值,可用於測試機器",
"rule.hardcodeTNTangle.extra.0": "默認為-1,必須在0與360之間,或者-1",
"rule.hopperCounters.name": "漏斗計數器",
"rule.hopperCounters.desc": "漏斗指向的羊毛方塊會計算吸收的物品並清除",
"rule.hopperCounters.extra.0": "在羊毛方塊上放置紅色或綠色地毯,使用/counter指令可啟用",
"rule.hopperCounters.extra.1": "使用 /counter <color?> reset重置計數器,使用/counter <color?>查詢資訊",
"rule.hopperCounters.extra.2": "在生存模式,在同色羊毛上放置綠色地毯可以查詢,放紅色地毯重置",
"rule.hopperCounters.extra.3": "數據是全伺服器玩家共享的,並且有16色可以使用",
"rule.hopperCounters.extra.4": "物品會被銷毀,每個漏斗每tick銷毀一組物品",
"rule.huskSpawningInTemples.name": "屍殼在沙漠神殿裡生成",
"rule.huskSpawningInTemples.desc": "只有屍殼會在沙漠神殿生成",
"rule.lagFreeSpawning.name": "延遲自然生成",
"rule.lagFreeSpawning.desc": "生物生成需要更少的CPU和RAM",
"rule.leadFix.name": "栓繩修復",
"rule.leadFix.desc": "修復栓繩在未加載區塊中,變得不可見的問題",
"rule.maxEntityCollisions.name": "最大實體擠壓數量",
"rule.maxEntityCollisions.desc": "設定最大實體擠壓限制,0表示無限大",
"rule.mergeTNT.name": "合併TNT",
"rule.mergeTNT.desc": "合併靜止的點燃的tnt實體",
"rule.missingTools.name": "工具缺失修復",
"rule.missingTools.desc": "活塞,玻璃和海綿可以使用合適的工具更快地破壞",
"rule.movableBlockEntities.name": "容器方塊移動",
"rule.movableBlockEntities.desc": "活塞可以推動具有容器標籤的方塊,比如箱子,漏斗",
"rule.onePlayerSleeping.name": "單個玩家睡覺跳過夜晚",
"rule.onePlayerSleeping.desc": "只要伺服器中其中一人睡覺即可跳過全伺服器夜晚",
"rule.optimizedTNT.name": "TNT優化",
"rule.optimizedTNT.desc": "tnt在相同位置或者在流體裡,爆炸將造成更低延遲",
"rule.persistentParrots.name": "鸚鵡停留",
"rule.persistentParrots.desc": "鸚鵡不會離開你的肩膀,除非你受到傷害",
"rule.placementRotationFix.name": "放置旋轉問題修復",
"rule.placementRotationFix.desc": "修復玩家放置方塊時快速轉身造成的問題",
"rule.pushLimit.name": "自訂義推動上限",
"rule.pushLimit.desc": "自訂義活塞推動上限",
"rule.pushLimit.extra.0": "你必須在1-1024中選取一個值,預設是12",
"rule.quasiConnectivity.name": "半連接激活開關",
"rule.quasiConnectivity.desc": "活塞,發射器和投擲器在他們上方方塊激活時是否響應",
"rule.quasiConnectivity.extra.0": "設置為true為開啟 設置為false為禁用",
"rule.railPowerLimit.name": "自訂義動力鐵軌激活距離",
"rule.railPowerLimit.desc": "動力鐵軌激活距離能在此更改",
"rule.railPowerLimit.extra.0": "你必須在1-1024中選取一個數值",
"rule.renewableCoral.name": "可再生珊瑚",
"rule.renewableCoral.desc": "使用骨粉可催熟珊瑚以長成珊瑚方塊結構",
"rule.renewableSponges.name": "可再生海綿",
"rule.renewableSponges.desc": "深海守衛將在被雷擊後變成遠古深海守衛",
"rule.rotatorBlock.name": "仙人掌扳手發射器版",
"rule.rotatorBlock.desc": "發射器中的仙人掌可以旋轉方塊(盡可能以逆時針)",
"rule.scriptsAutoload.name": "Scarpet腳本自動加載",
"rule.scriptsAutoload.desc": "在伺服器/世界加載的時候將會自動從世界文件中加載Scarpet腳本文件",
"rule.scriptsAutoload.extra.0": "/script指令必須為啟用狀態",
"rule.shulkerSpawningInEndCities.name": "可重生界伏蚌",
"rule.shulkerSpawningInEndCities.desc": "界伏蚌將會在終界城市中重生",
"rule.silverFishDropGravel.name": "蠹魚鑽出放塊掉落礫石",
"rule.silverFishDropGravel.desc": "蠹魚鑽出放塊時會掉落礫石",
"rule.smoothClientAnimations.name": "更平暢玩家端動畫",
"rule.smoothClientAnimations.desc": "降低TPS設置時可以使玩家端動畫更加平暢",
"rule.smoothClientAnimations.extra.0": "僅在單人模式中生效,並且會降低玩家端運行速度",
"rule.stackableShulkerBoxes.name": "可堆疊界伏盒",
"rule.stackableShulkerBoxes.desc": "空的界伏盒扔在地上時將會自動堆疊,直到堆疊為1組",
"rule.stackableShulkerBoxes.extra.0": "在物品GUI中,請使用shift鍵+單擊以移動整組界伏盒",
"rule.summonNaturalLightning.name": "召喚閃電自然化",
"rule.summonNaturalLightning.desc": "召喚的閃電將具有自然生成時的特性",
"rule.tntDoNotUpdate.name": "TNT放置時不會被更新",
"rule.tntDoNotUpdate.desc": "TNT在放置時不會產生任何方塊更新(不會激活)",
"rule.tntPrimerMomentumRemoved.name": "移除TNT點燃時隨機動量",
"rule.tntPrimerMomentumRemoved.desc": "TNT點燃時的隨機動量將被移除",
"rule.tntRandomRange.name": "TNT爆炸範圍設置",
"rule.tntRandomRange.desc": "設置TNT隨機爆炸範圍為一個固定的值,設為-1以禁用",
"rule.tntRandomRange.extra.0": "必須啟用optimizedTNT功能",
"rule.tntRandomRange.extra.1": "不能設置為負數,除了-1",
"rule.unloadedEntityFix.name": "未加載實體修復",
"rule.unloadedEntityFix.desc": "進入或者被推入未加載區塊的實體不會消失",
"rule.viewDistance.name": "自訂義視野",
"rule.viewDistance.desc": "改變伺服器端視野距離,設置為0以使用默認值",
"rule.viewDistance.extra.0": "你必須在0(使用伺服器默認設置)-32取一個整數",
"rule.xpNoCooldown.name": "經驗球吸收無冷卻",
"rule.xpNoCooldown.desc": "玩家吸收經驗球不會有冷卻時間",
"rule.portalSuffocationFix.name": "傳送門窒息修復",
"rule.portalSuffocationFix.desc": "使地獄傳送門正確地傳送實體",
"rule.portalSuffocationFix.extra.0": "傳送的實體將不再卡在黑曜石裡",
"rule.portalCreativeDelay.name": "創造模式玩家地獄門傳送延遲",
"rule.portalCreativeDelay.desc": "創造模式下使用地獄傳送門將同生存時產生延遲",
"category.bugfix": "BUG修復",
"category.survival": "生存用",
"category.creative": "創造用",
"category.experimental": "實驗用",
"category.optimization": "優化",
"category.feature": "特性",
"category.command": "指令",
"category.tnt": "TNT",
"category.dispenser": "發射器",
"category.scarpet": "scarpet腳本程式語言",
"category.client": "玩家端",
"ui.browse_categories": "瀏覽分類",
"ui.list_all_%(cat)s_settings": "列出所有 %s 選項",
"ui.version": "版本",
"ui.current_%(mod)s_settings": "目前啟用的 %s 選項",
"ui.switch_to": "切換至",
"ui.settings_are_locked": "選項被鎖定",
"ui.is_set_to": "被設置為",
"ui.unknown_rule": "未知的規則選項",
"ui.current_%(mod)s_startup_settings_from_%(conf)s": "目前啟用的%s選項,從%s中讀取得",
"ui.%(mod)s_settings_matching_'%(query)s'": "%s中符合\"%s\"的選項:",
"ui.all_%(mod)s_settings": "所有的%s選項",
"ui.tags": "分類",
"ui.change_permanently": "設為預設",
"ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts": "點擊此按鈕以使此選項保存於%s,重啟後依舊生效",
"ui.rule_%(rule)s_will_now_default_to_%(value)s": "規則%s將會被默認設置為%s",
"ui.current_value": "當前的值"
}
@@ -49,6 +49,11 @@ __command() ->
print(' "/camera show": Show current path particles');
print(' color of particles used is different for different players');
print(' "/camera hide": Hide path display');
print(' "/camera prefer_smooth_play": ');
print(' Eat CPU spikes and continue as usual');
print(' "/camera prefer_synced_play": ');
print(' After CPU spikes jump to where you should be');
print('');
''
);
@@ -483,6 +488,11 @@ hide() ->
);
// runs the player on the path
global_prefer_sync = false;
prefer_smooth_play() -> (global_prefer_sync = false; 'Smooth path play');
prefer_synced_play() -> (global_prefer_sync = true; 'Synchronized path play');
play() ->
(
if (err = __is_not_valid_for_motion(), exit(err));
@@ -501,6 +511,7 @@ play() ->
global_playing_path = true;
mspt = 1000 / 60;
start_time = time();
very_start = start_time;
point = 0;
p = __get_player();
try (
@@ -514,9 +525,14 @@ play() ->
modify(p, 'location', v);
point += 1;
end_time = time();
took = end_time - start_time;
if (took < mspt, sleep(mspt-took));
start_time = time()
if (global_prefer_sync,
should_be = very_start + mspt*point;
if (end_time < should_be, sleep(should_be-end_time) )
,
took = end_time - start_time;
if (took < mspt, sleep(mspt-took));
start_time = time()
)
)
);
);
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff. Show more