moved a bunch of stuff
This commit is contained in:
1 parent
72c276f976
commit
ecced620a4
30 files changed
+4450
-51
No files matched your search
@@ -1,13 +1,13 @@
|
||||
package carpet;
|
||||
|
||||
import carpet.commands.CameraModeCommand;
|
||||
//import carpet.commands.CarpetCommand;
|
||||
//import carpet.commands.CounterCommand;
|
||||
import carpet.commands.CarpetCommand;
|
||||
import carpet.commands.CounterCommand;
|
||||
//import carpet.commands.DistanceCommand;
|
||||
import carpet.commands.DrawCommand;
|
||||
//import carpet.commands.ScriptCommand;
|
||||
//import carpet.commands.InfoCommand;
|
||||
//import carpet.commands.PerimeterInfoCommand;
|
||||
import carpet.commands.PerimeterInfoCommand;
|
||||
//import carpet.commands.PlayerCommand;
|
||||
//import carpet.commands.SpawnCommand;
|
||||
import carpet.commands.LogCommand;
|
||||
@@ -20,6 +20,8 @@ import carpet.commands.TestCommand;
|
||||
import java.util.Random;
|
||||
|
||||
//import carpet.helpers.TickSpeed;
|
||||
import carpet.logging.LoggerRegistry;
|
||||
import carpet.utils.HUDController;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -34,32 +36,32 @@ public class CarpetServer // static for now - easier to handle all around the co
|
||||
}
|
||||
public static void onServerLoaded(MinecraftServer server)
|
||||
{
|
||||
//CarpetSettings.apply_settings_from_conf(server);
|
||||
CarpetSettings.apply_settings_from_conf(server);
|
||||
//ExpressionInspector.CarpetExpression_resetExpressionEngine();
|
||||
}
|
||||
// Separate from onServerLoaded, because a server can be loaded multiple times in singleplayer
|
||||
public static void onGameStarted() {
|
||||
//LoggerRegistry.initLoggers();
|
||||
LoggerRegistry.initLoggers();
|
||||
}
|
||||
|
||||
public static void tick(MinecraftServer server)
|
||||
{
|
||||
//TickSpeed.tick(server);
|
||||
//HUDController.update_hud(server);
|
||||
HUDController.update_hud(server);
|
||||
}
|
||||
|
||||
public static void registerCarpetCommands(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||
{
|
||||
//CarpetCommand.register(dispatcher);
|
||||
CarpetCommand.register(dispatcher);
|
||||
//TickCommand.register(dispatcher);
|
||||
//CounterCommand.register(dispatcher);
|
||||
CounterCommand.register(dispatcher);
|
||||
LogCommand.register(dispatcher);
|
||||
//SpawnCommand.register(dispatcher);
|
||||
//PlayerCommand.register(dispatcher);
|
||||
CameraModeCommand.register(dispatcher);
|
||||
//InfoCommand.register(dispatcher);
|
||||
//DistanceCommand.register(dispatcher);
|
||||
//PerimeterInfoCommand.register(dispatcher);
|
||||
PerimeterInfoCommand.register(dispatcher);
|
||||
DrawCommand.register(dispatcher);
|
||||
//ScriptCommand.register(dispatcher);
|
||||
|
||||
|
||||
@@ -179,33 +179,12 @@ public class CarpetSettings
|
||||
Messenger.print_server_message(CarpetServer.minecraft_server, "view distance can only be changed on a server");
|
||||
}
|
||||
}),
|
||||
/*rule("disableSpawnChunks", "creative", "Removes the spawn chunks.")
|
||||
rule("disableSpawnChunks", "creative", "Removes the spawn chunks.")
|
||||
.validate((s) -> {
|
||||
if (!CarpetSettings.getBool("disableSpawnChunks")) {
|
||||
ServerWorld overworld = CarpetServer.minecraft_server.getWorld(DimensionType.OVERWORLD);
|
||||
if (overworld == null)
|
||||
return;
|
||||
|
||||
List<ChunkPos> chunkList = SpawnChunks.listIncludedChunks(overworld);
|
||||
|
||||
// Reused from MinecraftServer initialWorldChunkLoad
|
||||
CompletableFuture<?> completablefuture = overworld.getChunkProvider().loadChunks(chunkList, (c) -> {});
|
||||
while (!completablefuture.isDone()) {
|
||||
try {
|
||||
completablefuture.get(1L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException interruptedexception) {
|
||||
throw new RuntimeException(interruptedexception);
|
||||
} catch (ExecutionException executionexception) {
|
||||
if (executionexception.getCause() instanceof RuntimeException) {
|
||||
throw (RuntimeException) executionexception.getCause();
|
||||
}
|
||||
|
||||
throw new RuntimeException(executionexception.getCause());
|
||||
} catch (TimeoutException var22) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}).boolAccelerate(),*/
|
||||
if (!CarpetSettings.getBool("disableSpawnChunks"))
|
||||
if (CarpetServer.minecraft_server != null)
|
||||
Messenger.print_server_message(CarpetServer.minecraft_server, "Spawn chunks re-enabled. Visit spawn to load it.");
|
||||
}).boolAccelerate(),
|
||||
rule("kelpGenerationGrowLimit", "feature", "limits growth limit of newly naturally generated kelp to this amount of blocks")
|
||||
.choices("25", "0 2 25").setNotStrict(),
|
||||
rule("renewableCoral", "feature", "Coral structures will grow with bonemeal from coral plants"),
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
package carpet.commands;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.CarpetSettings.CarpetSettingEntry;
|
||||
import carpet.utils.Messenger;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.chat.BaseComponent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
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 CarpetCommand
|
||||
{
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal("carpet").requires((player) ->
|
||||
player.hasPermissionLevel(2) && !CarpetSettings.locked);
|
||||
|
||||
literalargumentbuilder.executes((context)->listAllSettings(context.getSource())).
|
||||
then(literal("list").
|
||||
executes( (c) -> listSettings(c.getSource(),
|
||||
"All CarpetMod Settings",
|
||||
CarpetSettings.findAll(null))).
|
||||
then(literal("defaults").
|
||||
executes( (c)-> listSettings(c.getSource(),
|
||||
"Current CarpetMod Startup Settings from carpet.conf",
|
||||
CarpetSettings.findStartupOverrides(c.getSource().getMinecraftServer())))).
|
||||
then(argument("tag",StringArgumentType.word()).
|
||||
suggests( (c, b)->suggestMatching(CarpetSettings.default_tags, b)).
|
||||
executes( (c) -> listSettings(c.getSource(),
|
||||
String.format("CarpetMod Settings matching \"%s\"", StringArgumentType.getString(c, "tag")),
|
||||
CarpetSettings.findAll(StringArgumentType.getString(c, "tag"))))));
|
||||
|
||||
for (CarpetSettingEntry rule: CarpetSettings.getAllCarpetSettings())
|
||||
{
|
||||
literalargumentbuilder.then(literal(rule.getName()).executes( (context) ->
|
||||
displayRuleMenu(context.getSource(),rule)));
|
||||
literalargumentbuilder.then(literal("removeDefault").
|
||||
then(literal(rule.getName()).executes((context) ->
|
||||
removeDefault(context.getSource(), rule))));
|
||||
literalargumentbuilder.then(literal(rule.getName()).
|
||||
then(argument("value", StringArgumentType.word()).
|
||||
suggests((c, b)-> suggestMatching(rule.getOptions(),b)).
|
||||
executes((context) ->
|
||||
setRule(context.getSource(), rule, StringArgumentType.getString(context, "value")))));
|
||||
literalargumentbuilder.then(literal("setDefault").
|
||||
then(literal(rule.getName()).
|
||||
then(argument("value", StringArgumentType.word()).
|
||||
suggests((c, b)-> suggestMatching(rule.getOptions(),b)).
|
||||
executes((context) ->
|
||||
setDefault(context.getSource(), rule, StringArgumentType.getString(context, "value"))))));
|
||||
}
|
||||
dispatcher.register(literalargumentbuilder);
|
||||
}
|
||||
|
||||
private static int displayRuleMenu(ServerCommandSource source, CarpetSettingEntry rule)
|
||||
{
|
||||
PlayerEntity player;
|
||||
try
|
||||
{
|
||||
player = source.getPlayer();
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
Messenger.m(source, "w "+rule.getName() +" is set to: ","wb "+rule.getStringValue());
|
||||
return 1;
|
||||
}
|
||||
|
||||
Messenger.m(player, "");
|
||||
Messenger.m(player, "wb "+rule.getName(),"!/carpet "+rule.getName(),"^g refresh");
|
||||
Messenger.m(player, "w "+rule.getToast());
|
||||
|
||||
Arrays.stream(rule.getInfo()).forEach(s -> Messenger.m(player, "g "+s));
|
||||
|
||||
List<BaseComponent> tags = new ArrayList<>();
|
||||
tags.add(Messenger.c("w Tags: "));
|
||||
for (String t: rule.getTags())
|
||||
{
|
||||
tags.add(Messenger.c("c ["+t+"]", "^g list all "+t+" settings","!/carpet 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.getStringValue(),rule.isDefault()?"default":"modified"));
|
||||
List<BaseComponent> options = new ArrayList<>();
|
||||
options.add(Messenger.c("w Options: ", "y [ "));
|
||||
for (String o: rule.getOptions())
|
||||
{
|
||||
options.add(Messenger.c(
|
||||
String.format("%s%s %s",(o.equals(rule.getDefault()))?"u":"", (o.equals(rule.getStringValue()))?"bl":"y", o ),
|
||||
"^g switch to "+o,
|
||||
String.format("?/carpet %s %s",rule.getName(),o)));
|
||||
options.add(Messenger.c("w "));
|
||||
}
|
||||
options.remove(options.size()-1);
|
||||
options.add(Messenger.c("y ]"));
|
||||
Messenger.m(player, options.toArray(new Object[0]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int setRule(ServerCommandSource source, CarpetSettingEntry rule, String newValue)
|
||||
{
|
||||
CarpetSettings.set(rule.getName(), newValue);
|
||||
Messenger.m(source, "w "+rule.toString()+", ", "c [change permanently?]",
|
||||
"^w Click to keep the settings in carpet.conf to save across restarts",
|
||||
"?/carpet setDefault "+rule.getName()+" "+rule.getStringValue());
|
||||
return 1;
|
||||
}
|
||||
private static int setDefault(ServerCommandSource source, CarpetSettingEntry rule, String defaultValue)
|
||||
{
|
||||
CarpetSettings.setDefaultRule(source.getMinecraftServer(), rule.getName(), defaultValue);
|
||||
Messenger.m(source ,"gi rule "+ rule.getName()+" will now default to "+ defaultValue);
|
||||
return 1;
|
||||
}
|
||||
private static int removeDefault(ServerCommandSource source, CarpetSettingEntry rule)
|
||||
{
|
||||
CarpetSettings.removeDefaultRule(source.getMinecraftServer(), rule.getName());
|
||||
Messenger.m(source ,"gi rule "+ rule.getName()+" defaults to Vanilla");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
private static BaseComponent displayInteractiveSetting(CarpetSettingEntry e)
|
||||
{
|
||||
List<Object> args = new ArrayList<>();
|
||||
args.add("w - "+e.getName()+" ");
|
||||
args.add("!/carpet "+e.getName());
|
||||
args.add("^y "+e.getToast());
|
||||
for (String option: e.getOptions())
|
||||
{
|
||||
String style = e.isDefault()?"g":(option.equalsIgnoreCase(e.getDefault())?"y":"e");
|
||||
if (option.equalsIgnoreCase(e.getDefault()))
|
||||
style = style+"b";
|
||||
else if (option.equalsIgnoreCase(e.getStringValue()))
|
||||
style = style+"u";
|
||||
args.add(style+" ["+option+"]");
|
||||
if (!option.equalsIgnoreCase(e.getStringValue()))
|
||||
{
|
||||
args.add("!/carpet " + e.getName() + " " + option);
|
||||
args.add("^w switch to " + option);
|
||||
}
|
||||
args.add("w ");
|
||||
}
|
||||
args.remove(args.size()-1);
|
||||
return Messenger.c(args.toArray(new Object[0]));
|
||||
}
|
||||
|
||||
private static int listSettings(ServerCommandSource source, String title, CarpetSettingEntry[] settings_list)
|
||||
{
|
||||
try
|
||||
{
|
||||
PlayerEntity player = source.getPlayer();
|
||||
Messenger.m(player,String.format("wb %s:",title));
|
||||
Arrays.stream(settings_list).forEach(e -> Messenger.m(player,displayInteractiveSetting(e)));
|
||||
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
Messenger.m(source, "w s:"+title);
|
||||
Arrays.stream(settings_list).forEach(r -> Messenger.m(source, "w - "+ r.toString()));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
private static int listAllSettings(ServerCommandSource source)
|
||||
{
|
||||
listSettings(source, "Current CarpetMod Settings", CarpetSettings.find_nondefault(source.getMinecraftServer()));
|
||||
|
||||
Messenger.m(source, "Carpet Mod version: "+CarpetSettings.carpetVersion);
|
||||
try
|
||||
{
|
||||
PlayerEntity player = source.getPlayer();
|
||||
List<Object> tags = new ArrayList<>();
|
||||
tags.add("w Browse Categories:\n");
|
||||
for (String t : CarpetSettings.default_tags)
|
||||
{
|
||||
tags.add("c [" + t+"]");
|
||||
tags.add("^g list all " + t + " settings");
|
||||
tags.add("!/carpet list " + t);
|
||||
tags.add("w ");
|
||||
}
|
||||
tags.remove(tags.size() - 1);
|
||||
Messenger.m(player, tags.toArray(new Object[0]));
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package carpet.commands;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.helpers.HopperCounter;
|
||||
import carpet.utils.Messenger;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.command.CommandException;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.util.DyeColor;
|
||||
import net.minecraft.network.chat.BaseComponent;
|
||||
|
||||
public class CounterCommand
|
||||
{
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = CommandManager.literal("counter").executes((context)
|
||||
-> listAllCounters(context.getSource(), false)).requires((player) ->
|
||||
CarpetSettings.getBool("hopperCounters"));
|
||||
|
||||
literalargumentbuilder.
|
||||
then((CommandManager.literal("reset").executes( (p_198489_1_)->
|
||||
resetCounter(p_198489_1_.getSource(), null))));
|
||||
for (DyeColor enumDyeColor: DyeColor.values())
|
||||
{
|
||||
String color = enumDyeColor.toString();
|
||||
literalargumentbuilder.
|
||||
then((CommandManager.literal(color).executes( (p_198489_1_)-> displayCounter(p_198489_1_.getSource(), color, false))));
|
||||
literalargumentbuilder.then(CommandManager.literal(color).
|
||||
then(CommandManager.literal("reset").executes((context) ->
|
||||
resetCounter(context.getSource(), color))));
|
||||
literalargumentbuilder.then(CommandManager.literal(color).
|
||||
then(CommandManager.literal("realtime").executes((context) ->
|
||||
displayCounter(context.getSource(), color, true))));
|
||||
}
|
||||
dispatcher.register(literalargumentbuilder);
|
||||
}
|
||||
|
||||
private static int displayCounter(ServerCommandSource source, String color, boolean realtime)
|
||||
{
|
||||
HopperCounter counter = HopperCounter.getCounter(color);
|
||||
if (counter == null) throw new CommandException(Messenger.s("Unknown wool color: "+color));
|
||||
|
||||
for (BaseComponent message: counter.format(source.getMinecraftServer(), realtime, false))
|
||||
{
|
||||
source.sendFeedback(message, false);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int resetCounter(ServerCommandSource source, String color)
|
||||
{
|
||||
if (color == null)
|
||||
{
|
||||
HopperCounter.resetAll(source.getMinecraftServer());
|
||||
Messenger.m(source, "w Restarted all counters");
|
||||
}
|
||||
else
|
||||
{
|
||||
HopperCounter counter = HopperCounter.getCounter(color);
|
||||
if (counter == null) throw new CommandException(Messenger.s("Unknown wool color"));
|
||||
counter.reset(source.getMinecraftServer());
|
||||
Messenger.m(source, "w Restarted "+color+" counter");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int listAllCounters(ServerCommandSource source, boolean realtime)
|
||||
{
|
||||
for (BaseComponent message: HopperCounter.formatAll(source.getMinecraftServer(), realtime))
|
||||
{
|
||||
source.sendFeedback(message, false);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package carpet.commands;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.utils.Messenger;
|
||||
import carpet.utils.PerimeterDiagnostics;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.command.suggestion.SuggestionProviders;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.mob.MobEntity;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.command.arguments.BlockPosArgumentType;
|
||||
import net.minecraft.command.arguments.EntitySummonArgumentType;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class PerimeterInfoCommand
|
||||
{
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> command = literal("perimeterinfo").
|
||||
requires((player) -> CarpetSettings.getBool("commandPerimeterInfo")).
|
||||
executes( (c) -> perimeterDiagnose(
|
||||
c.getSource(),
|
||||
new BlockPos(c.getSource().getPosition()),
|
||||
null)).
|
||||
then(argument("center position", BlockPosArgumentType.create()).
|
||||
executes( (c) -> perimeterDiagnose(
|
||||
c.getSource(),
|
||||
BlockPosArgumentType.getBlockPos(c, "center position"),
|
||||
null)).
|
||||
then(argument("mob",EntitySummonArgumentType.create()).
|
||||
suggests(SuggestionProviders.SUMMONABLE_ENTITIES).
|
||||
executes( (c) -> perimeterDiagnose(
|
||||
c.getSource(),
|
||||
BlockPosArgumentType.getBlockPos(c, "center position"),
|
||||
EntitySummonArgumentType.getEntitySummon(c, "mob").toString()
|
||||
))));
|
||||
dispatcher.register(command);
|
||||
}
|
||||
|
||||
private static int perimeterDiagnose(ServerCommandSource source, BlockPos pos, String mobId)
|
||||
{
|
||||
CompoundTag nbttagcompound = new CompoundTag();
|
||||
MobEntity entityliving = null;
|
||||
if (mobId != null)
|
||||
{
|
||||
nbttagcompound.putString("id", mobId);
|
||||
entityliving = (MobEntity) EntityType.loadEntityWithPassengers(nbttagcompound, source.getWorld(), (entity_1x) -> {
|
||||
entity_1x.setPositionAndAngles(pos.getX(), pos.getY(), pos.getZ(), entity_1x.yaw, entity_1x.pitch);
|
||||
return !source.getWorld().method_18768(entity_1x) ? null : entity_1x;
|
||||
});
|
||||
if (entityliving == null)
|
||||
{
|
||||
Messenger.m(source, "r Failed to spawn test entity");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
PerimeterDiagnostics.Result res = PerimeterDiagnostics.countSpots(source.getWorld(), pos, entityliving);
|
||||
|
||||
Messenger.m(source, "w Spawning spaces around ",Messenger.tp("c",pos), "w :");
|
||||
Messenger.m(source, "w potential in-liquid: ","wb "+res.liquid);
|
||||
Messenger.m(source, "w potential on-ground: ","wb "+res.ground);
|
||||
if (entityliving != null)
|
||||
{
|
||||
Messenger.m(source, "w ", entityliving.getDisplayName() ,"w : ","wb "+res.specific);
|
||||
res.samples.forEach(bp -> Messenger.m(source, "w ", Messenger.tp("c", bp)));
|
||||
entityliving.remove();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
package carpet.commands;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.patches.EntityPlayerMPFake;
|
||||
import carpet.utils.Messenger;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.command.suggestion.SuggestionProviders;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.math.Vec2f;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.GameType;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class PlayerCommand
|
||||
{
|
||||
ServerPlayerEntity
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal("player").
|
||||
requires((player) -> CarpetSettings.getBool("commandPlayer")).
|
||||
then(argument("player", word()).
|
||||
suggests( (c, b) -> SuggestionProviders.suggest(getPlayers(c.getSource()), b)).
|
||||
then(literal("stop").
|
||||
executes(PlayerCommand::stop)).
|
||||
then(literal("use").
|
||||
executes(PlayerCommand::useOnce).
|
||||
then(literal("once").
|
||||
executes( PlayerCommand::useOnce)).
|
||||
then(literal("continuous").
|
||||
executes( PlayerCommand::useContinuous)).
|
||||
then(literal("interval").
|
||||
then(argument("ticks",integer(2)).
|
||||
executes( PlayerCommand::useInterval)))).
|
||||
then(literal("jump").
|
||||
executes( PlayerCommand::jumpOnce).
|
||||
then(literal("once").
|
||||
executes( PlayerCommand::jumpOnce)).
|
||||
then(literal("continuous").
|
||||
executes( PlayerCommand::jumpContinuous)).
|
||||
then(literal("interval").
|
||||
then(argument("ticks",integer(2)).
|
||||
executes( PlayerCommand::jumpInterval )))).
|
||||
then(literal("attack").
|
||||
executes(PlayerCommand::attackOnce).
|
||||
then(literal("once").
|
||||
executes(PlayerCommand::attackOnce)).
|
||||
then(literal("continuous").
|
||||
executes(PlayerCommand::attackContinuous)).
|
||||
then(literal("interval").
|
||||
then(argument("ticks",integer(2)).
|
||||
executes(PlayerCommand::attackInterval)))).
|
||||
then(literal("drop").
|
||||
executes(PlayerCommand::dropItem)).
|
||||
then(literal("swapHands").
|
||||
executes(PlayerCommand::swapHands)).
|
||||
then(literal("kill").
|
||||
executes(PlayerCommand::kill)).
|
||||
then(literal("shadow").
|
||||
executes(PlayerCommand::shadow)).
|
||||
then(literal("mount").
|
||||
executes(PlayerCommand::mount)).
|
||||
then(literal("dismount").
|
||||
executes(PlayerCommand::dismount)).
|
||||
then(literal("sneak").
|
||||
executes(PlayerCommand::sneak)).
|
||||
then(literal("sprint").
|
||||
executes(PlayerCommand::sprint)).
|
||||
then(literal("look").
|
||||
then(literal("north").
|
||||
executes(PlayerCommand::lookNorth)).
|
||||
then(literal("south").
|
||||
executes(PlayerCommand::lookSouth)).
|
||||
then(literal("east").
|
||||
executes(PlayerCommand::lookEast)).
|
||||
then(literal("west").
|
||||
executes(PlayerCommand::lookWest)).
|
||||
then(literal("up").
|
||||
executes(PlayerCommand::lookUp)).
|
||||
then(literal("down").
|
||||
executes(PlayerCommand::lookDown)).
|
||||
then(argument("direction",RotationArgument.rotation()).
|
||||
executes(PlayerCommand::lookAround))).
|
||||
then(literal("turn").
|
||||
then(literal("left").
|
||||
executes(PlayerCommand::turnLeft)).
|
||||
then(literal("right").
|
||||
executes(PlayerCommand::turnRight)).
|
||||
then(literal("back").
|
||||
executes(PlayerCommand::turnBack)).
|
||||
then(argument("direction",RotationArgument.rotation()).
|
||||
executes(PlayerCommand::turn))).
|
||||
then(literal("move").
|
||||
then(literal("forward").
|
||||
executes(PlayerCommand::moveForward)).
|
||||
then(literal("backward").
|
||||
executes(PlayerCommand::moveBackward)).
|
||||
then(literal("left").
|
||||
executes(PlayerCommand::strafeLeft)).
|
||||
then(literal("right").
|
||||
executes(PlayerCommand::strafeRight))).
|
||||
then(literal("spawn").
|
||||
executes(PlayerCommand::spawn).
|
||||
then(literal("at").
|
||||
then(argument("position", Vec3Argument.vec3()).
|
||||
executes(PlayerCommand::spawn).
|
||||
then(literal("facing").
|
||||
then(argument("direction",RotationArgument.rotation()).
|
||||
executes(PlayerCommand::spawn).
|
||||
then(literal("in").
|
||||
then(argument("dimension",DimensionArgument.getDimension()).
|
||||
executes(PlayerCommand::spawn)))))))));
|
||||
|
||||
dispatcher.register(literalargumentbuilder);
|
||||
}
|
||||
private static Collection<String> getPlayers(ServerCommandSource source)
|
||||
{
|
||||
Set<String> players = Sets.newLinkedHashSet( Arrays.asList("Steve","Alex"));
|
||||
players.addAll(source.getPlayerNames());
|
||||
return players;
|
||||
}
|
||||
|
||||
private static ServerPlayerEntityy getPlayer(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
String playerName = getString(context,"player");
|
||||
MinecraftServer server = context.getSource().getServer();
|
||||
return (ServerPlayerEntityy) server.getPlayerList().getPlayerByUsername(playerName);
|
||||
}
|
||||
|
||||
private static boolean cantSpawn(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
String playerName = getString(context,"player");
|
||||
PlayerEntity player = context.getSource().getServer().getPlayerList().getPlayerByUsername(playerName);
|
||||
if (player != null)
|
||||
{
|
||||
Messenger.m(context.getSource(), "r Player ", "rb "+playerName, "r is already logged on");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean cantManipulate(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
PlayerEntity player = getPlayer(context);
|
||||
if (player == null)
|
||||
{
|
||||
Messenger.m(context.getSource(),"r Can only manipulate existing players");
|
||||
return true;
|
||||
}
|
||||
PlayerEntity sendingPlayer;
|
||||
try
|
||||
{
|
||||
sendingPlayer = context.getSource().asPlayer();
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(context.getSource().getServer().getPlayerList().canSendCommands(sendingPlayer.getGameProfile())))
|
||||
{
|
||||
if (!(sendingPlayer == player || player instanceof EntityPlayerMPFake))
|
||||
{
|
||||
Messenger.m(context.getSource(),"r Non OP players can't control other real players");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean cantShadow(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context))
|
||||
return true;
|
||||
PlayerEntity player = getPlayer(context);
|
||||
if (player instanceof EntityPlayerMPFake)
|
||||
{
|
||||
Messenger.m(context.getSource(), "r Only real players can be shadowed");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean cantReMove(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context))
|
||||
return true;
|
||||
PlayerEntity player = getPlayer(context);
|
||||
if (!(player instanceof EntityPlayerMPFake))
|
||||
{
|
||||
Messenger.m(context.getSource(), "r Only fake players can be moved or killed");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int stop(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.stop();
|
||||
return 1;
|
||||
}
|
||||
private static int useOnce(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.useOnce();
|
||||
return 1;
|
||||
}
|
||||
private static int useContinuous(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.setUseForever();
|
||||
return 1;
|
||||
}
|
||||
private static int useInterval(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
int ticks = getInteger(context, "ticks");
|
||||
getPlayer(context).actionPack.setUse(ticks, 0);
|
||||
return 1;
|
||||
}
|
||||
private static int jumpOnce(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.jumpOnce();
|
||||
return 1;
|
||||
}
|
||||
private static int jumpContinuous(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.setJumpForever();
|
||||
return 1;
|
||||
}
|
||||
private static int jumpInterval(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
int ticks = getInteger(context, "ticks");
|
||||
getPlayer(context).actionPack.setJump(ticks, 0);
|
||||
return 1;
|
||||
}
|
||||
private static int attackOnce(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.attackOnce();
|
||||
getPlayer(context).actionPack.resetBlockRemoving(true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
private static int attackContinuous(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.setAttackForever();
|
||||
return 1;
|
||||
}
|
||||
private static int attackInterval(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
int ticks = getInteger(context, "ticks");
|
||||
getPlayer(context).actionPack.setAttack(ticks, 0);
|
||||
return 1;
|
||||
}
|
||||
private static int dropItem(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.dropItem();
|
||||
return 1;
|
||||
}
|
||||
private static int swapHands(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.swapHands();
|
||||
return 1;
|
||||
}
|
||||
private static int kill(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).onKillCommand();
|
||||
return 1;
|
||||
}
|
||||
private static int shadow(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantShadow(context)) return 0;
|
||||
EntityPlayerMPFake.createShadow(
|
||||
context.getSource().getServer(),
|
||||
getPlayer(context));
|
||||
return 1;
|
||||
}
|
||||
private static int mount(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.mount();
|
||||
return 1;
|
||||
}
|
||||
private static int dismount(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantManipulate(context)) return 0;
|
||||
getPlayer(context).actionPack.dismount();
|
||||
return 1;
|
||||
}
|
||||
private static int sneak(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.setSneaking(true);
|
||||
return 1;
|
||||
}
|
||||
private static int sprint(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.setSprinting(true);
|
||||
return 1;
|
||||
}
|
||||
private static int lookNorth(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.look("north");
|
||||
return 1;
|
||||
}
|
||||
private static int lookSouth(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.look("south");
|
||||
return 1;
|
||||
}
|
||||
private static int lookEast(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.look("east");
|
||||
return 1;
|
||||
}
|
||||
private static int lookWest(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.look("west");
|
||||
return 1;
|
||||
}
|
||||
private static int lookUp(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.look("up");
|
||||
return 1;
|
||||
}
|
||||
private static int lookDown(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.look("down");
|
||||
return 1;
|
||||
}
|
||||
private static int lookAround(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
Vec2f vec2f = RotationArgument.getRotation(context, "direction").getRotation(context.getSource());
|
||||
getPlayer(context).actionPack.look(vec2f.y,vec2f.x);
|
||||
return 1;
|
||||
}
|
||||
private static int turnRight(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.turn("right");
|
||||
return 1;
|
||||
}
|
||||
private static int turnLeft(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.turn("left");
|
||||
return 1;
|
||||
}
|
||||
private static int turnBack(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.turn("back");
|
||||
return 1;
|
||||
}
|
||||
private static int turn(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
Vec2f vec2f = RotationArgument.getRotation(context, "direction").getRotation(context.getSource());
|
||||
getPlayer(context).actionPack.turn(vec2f.y,vec2f.x);
|
||||
return 1;
|
||||
}
|
||||
private static int moveForward(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.setForward(1.0F);
|
||||
return 1;
|
||||
}
|
||||
private static int moveBackward(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.setForward(-1.0F);
|
||||
return 1;
|
||||
}
|
||||
private static int strafeLeft(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.setStrafing(-1.0F);
|
||||
return 1;
|
||||
}
|
||||
private static int strafeRight(CommandContext<ServerCommandSource> context)
|
||||
{
|
||||
if (cantReMove(context)) return 0;
|
||||
getPlayer(context).actionPack.setStrafing(1.0F);
|
||||
return 1;
|
||||
}
|
||||
private static int spawn(CommandContext<ServerCommandSource> context) throws CommandSyntaxException
|
||||
{
|
||||
if (cantSpawn(context)) return 0;
|
||||
Vec3d pos;
|
||||
Vec2f facing;
|
||||
DimensionType dim;
|
||||
try
|
||||
{
|
||||
pos = Vec3Argument.getVec3(context, "position");
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
pos = context.getSource().getPos();
|
||||
}
|
||||
try
|
||||
{
|
||||
facing = RotationArgument.getRotation(context, "direction").getRotation(context.getSource());
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
facing = context.getSource().getRotation();
|
||||
}
|
||||
try
|
||||
{
|
||||
dim = DimensionArgument.func_212592_a(context, "dimension");
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
dim = context.getSource().getWorld().dimension.getType();
|
||||
}
|
||||
GameType mode = GameType.CREATIVE;
|
||||
try
|
||||
{
|
||||
ServerPlayerEntityy player = context.getSource().asPlayer();
|
||||
mode = player.interactionManager.getGameType();
|
||||
}
|
||||
catch (CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
PlayerEntity p = EntityPlayerMPFake.createFake(
|
||||
getString(context,"player"),
|
||||
context.getSource().getServer(),
|
||||
pos.x, pos.y, pos.z,
|
||||
facing.y, facing.x,
|
||||
dim,
|
||||
mode);
|
||||
if (p == null)
|
||||
{
|
||||
Messenger.m(context.getSource(), "rb Player "+getString(context,"player")+" doesn't exist " +
|
||||
"and cannot spawn in online mode. Turn the server offline to spawn non-existing players");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package carpet.commands;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.helpers.HopperCounter;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.utils.Messenger;
|
||||
import carpet.utils.SpawnReporter;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.command.arguments.BlockPosArgumentType;
|
||||
import net.minecraft.command.arguments.DimensionArgumentType;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.DyeColor;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.string;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
||||
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 SpawnCommand
|
||||
{
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal("spawn").
|
||||
requires((player) -> CarpetSettings.getBool("commandSpawn"));
|
||||
|
||||
literalargumentbuilder.
|
||||
then(literal("list").
|
||||
then(argument("pos", BlockPosArgumentType.create()).
|
||||
executes( (c) -> listSpawns(c.getSource(), BlockPosArgumentType.getBlockPos(c, "pos"))))).
|
||||
then(literal("tracking").
|
||||
executes( (c) -> printTrackingReport(c.getSource())).
|
||||
then(literal("start").
|
||||
executes( (c) -> startTracking(c.getSource(), null, null)).
|
||||
then(argument("from", BlockPosArgumentType.create()).
|
||||
then(argument("to", BlockPosArgumentType.create()).
|
||||
executes( (c) -> startTracking(
|
||||
c.getSource(),
|
||||
BlockPosArgumentType.getBlockPos(c, "from"),
|
||||
BlockPosArgumentType.getBlockPos(c, "to")))))).
|
||||
then(literal("stop").
|
||||
executes( (c) -> stopTracking(c.getSource()))).
|
||||
then(argument("type", word()).
|
||||
suggests( (c, b) -> suggestMatching(SpawnReporter.mob_groups,b)).
|
||||
executes( (c) -> recentSpawnsForType(c.getSource(), getString(c, "type"))))).
|
||||
then(literal("test").
|
||||
executes( (c)-> runTest(c.getSource(), 72000, null)).
|
||||
then(argument("ticks", integer(10,720000)).
|
||||
executes( (c)-> runTest(
|
||||
c.getSource(),
|
||||
getInteger(c, "ticks"),
|
||||
null)).
|
||||
then(argument("counter", word()).
|
||||
suggests( (c, b) -> suggestMatching(Arrays.stream(DyeColor.values()).map(DyeColor::toString),b)).
|
||||
executes((c)-> runTest(
|
||||
c.getSource(),
|
||||
getInteger(c, "ticks"),
|
||||
getString(c, "counter")))))).
|
||||
then(literal("mocking").
|
||||
then(argument("to do or not to do?", BoolArgumentType.bool()).
|
||||
executes( (c) -> toggleMocking(c.getSource(), BoolArgumentType.getBool(c, "to do or not to do?"))))).
|
||||
then(literal("rates").
|
||||
executes( (c) -> generalMobcaps(c.getSource())).
|
||||
then(literal("reset").
|
||||
executes( (c) -> resetSpawnRates(c.getSource()))).
|
||||
then(argument("type", word()).
|
||||
suggests( (c, b) -> suggestMatching(SpawnReporter.mob_groups,b)).
|
||||
then(argument("rounds", integer(0)).
|
||||
suggests( (c, b) -> suggestMatching(new String[]{"1"},b)).
|
||||
executes( (c) -> setSpawnRates(
|
||||
c.getSource(),
|
||||
getString(c, "type"),
|
||||
getInteger(c, "rounds")))))).
|
||||
then(literal("mobcaps").
|
||||
executes( (c) -> generalMobcaps(c.getSource())).
|
||||
then(literal("set").
|
||||
then(argument("cap (hostile)", integer(1,1400)).
|
||||
executes( (c) -> setMobcaps(c.getSource(), getInteger(c, "cap (hostile)"))))).
|
||||
then(argument("dimension", DimensionArgumentType.create()).
|
||||
executes( (c)-> mobcapsForDimension(c.getSource(), DimensionArgumentType.getDimensionArgument(c, "dimension"))))).
|
||||
then(literal("entities").
|
||||
executes( (c) -> generalMobcaps(c.getSource()) ).
|
||||
then(argument("type", string()).
|
||||
suggests( (c, b)->suggestMatching(SpawnReporter.mob_groups, b)).
|
||||
executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type")))));
|
||||
|
||||
dispatcher.register(literalargumentbuilder);
|
||||
}
|
||||
|
||||
private static int listSpawns(ServerCommandSource source, BlockPos pos)
|
||||
{
|
||||
Messenger.send(source, SpawnReporter.report(pos, source.getWorld()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int printTrackingReport(ServerCommandSource source)
|
||||
{
|
||||
Messenger.send(source, SpawnReporter.tracking_report(source.getWorld()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int startTracking(ServerCommandSource source, BlockPos a, BlockPos b)
|
||||
{
|
||||
if (SpawnReporter.track_spawns != 0L)
|
||||
{
|
||||
Messenger.m(source, "r You are already tracking spawning.");
|
||||
return 0;
|
||||
}
|
||||
BlockPos lsl = null;
|
||||
BlockPos usl = null;
|
||||
if (a != null && b != null)
|
||||
{
|
||||
lsl = new BlockPos(
|
||||
Math.min(a.getX(), b.getX()),
|
||||
Math.min(a.getY(), b.getY()),
|
||||
Math.min(a.getZ(), b.getZ()) );
|
||||
usl = new BlockPos(
|
||||
Math.max(a.getX(), b.getX()),
|
||||
Math.max(a.getY(), b.getY()),
|
||||
Math.max(a.getZ(), b.getZ()) );
|
||||
}
|
||||
SpawnReporter.reset_spawn_stats(false);
|
||||
SpawnReporter.track_spawns = (long) source.getMinecraftServer().getTicks();
|
||||
SpawnReporter.lower_spawning_limit = lsl;
|
||||
SpawnReporter.upper_spawning_limit = usl;
|
||||
Messenger.m(source, "gi Spawning tracking started.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int stopTracking(ServerCommandSource source)
|
||||
{
|
||||
Messenger.send(source, SpawnReporter.tracking_report(source.getWorld()));
|
||||
SpawnReporter.reset_spawn_stats(false);
|
||||
SpawnReporter.track_spawns = 0L;
|
||||
SpawnReporter.lower_spawning_limit = null;
|
||||
SpawnReporter.upper_spawning_limit = null;
|
||||
Messenger.m(source, "gi Spawning tracking stopped.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int recentSpawnsForType(ServerCommandSource source, String mob_type)
|
||||
{
|
||||
if (!Arrays.asList(SpawnReporter.mob_groups).contains(mob_type))
|
||||
{
|
||||
Messenger.m(source, "r Wrong mob type: "+mob_type);
|
||||
return 0;
|
||||
}
|
||||
Messenger.send(source, SpawnReporter.recent_spawns(source.getWorld(), mob_type));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int runTest(ServerCommandSource source, int ticks, String counter)
|
||||
{
|
||||
//stop tracking
|
||||
SpawnReporter.reset_spawn_stats(false);
|
||||
//start tracking
|
||||
SpawnReporter.track_spawns = (long) source.getMinecraftServer().getTicks();
|
||||
//counter reset
|
||||
if (counter == null)
|
||||
{
|
||||
HopperCounter.resetAll(source.getMinecraftServer());
|
||||
}
|
||||
else
|
||||
{
|
||||
HopperCounter hCounter = HopperCounter.getCounter(counter);
|
||||
if (hCounter != null)
|
||||
hCounter.reset(source.getMinecraftServer());
|
||||
}
|
||||
|
||||
|
||||
// tick warp 0
|
||||
TickSpeed.tickrate_advance(null, 0, null, null);
|
||||
// tick warp given player
|
||||
ServerCommandSource csource = null;
|
||||
PlayerEntity player = null;
|
||||
try
|
||||
{
|
||||
player = source.getPlayer();
|
||||
csource = source;
|
||||
}
|
||||
catch (CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
TickSpeed.tickrate_advance(player, ticks, null, csource);
|
||||
Messenger.m(source, String.format("gi Started spawn test for %d ticks", ticks));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int toggleMocking(ServerCommandSource source, boolean domock)
|
||||
{
|
||||
if (domock)
|
||||
{
|
||||
SpawnReporter.initialize_mocking();
|
||||
Messenger.m(source, "gi Mock spawns started, Spawn statistics reset");
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnReporter.stop_mocking();
|
||||
Messenger.m(source, "gi Normal mob spawning, Spawn statistics reset");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int generalMobcaps(ServerCommandSource source)
|
||||
{
|
||||
Messenger.send(source, SpawnReporter.printMobcapsForDimension(source.getWorld().getDimension().getType().getRawId()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int resetSpawnRates(ServerCommandSource source)
|
||||
{
|
||||
for (String s: SpawnReporter.spawn_tries.keySet())
|
||||
{
|
||||
SpawnReporter.spawn_tries.put(s,1);
|
||||
}
|
||||
Messenger.m(source, "gi Spawn rates brought to 1 round per tick for all groups.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int setSpawnRates(ServerCommandSource source, String mobtype, int rounds)
|
||||
{
|
||||
String code = SpawnReporter.get_creature_code_from_string(mobtype);
|
||||
SpawnReporter.spawn_tries.put(code, rounds);
|
||||
Messenger.m(source, "gi "+mobtype+" mobs will now spawn "+rounds+" times per tick");
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int setMobcaps(ServerCommandSource source, int hostile_cap)
|
||||
{
|
||||
double desired_ratio = (double)hostile_cap/ EntityCategory.MONSTER.getSpawnCap();
|
||||
SpawnReporter.mobcap_exponent = 4.0*Math.log(desired_ratio)/Math.log(2.0);
|
||||
Messenger.m(source, String.format("gi Mobcaps for hostile mobs changed to %d, other groups will follow", hostile_cap));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int mobcapsForDimension(ServerCommandSource source, DimensionType dim)
|
||||
{
|
||||
Messenger.send(source, SpawnReporter.printMobcapsForDimension(dim.getRawId()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int listEntitiesOfType(ServerCommandSource source, String mobtype)
|
||||
{
|
||||
Messenger.send(source, SpawnReporter.printEntitiesByType(mobtype, source.getWorld()));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package carpet.commands;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.utils.CarpetProfiler;
|
||||
import carpet.utils.Messenger;
|
||||
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.network.chat.BaseComponent;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
|
||||
import static com.mojang.brigadier.arguments.FloatArgumentType.floatArg;
|
||||
import static com.mojang.brigadier.arguments.FloatArgumentType.getFloat;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
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 TickCommand
|
||||
{
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher)
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> literalargumentbuilder = literal("tick").
|
||||
requires((player) -> CarpetSettings.getBool("commandTick")).
|
||||
then(literal("rate").
|
||||
executes((c) -> queryTps(c.getSource())).
|
||||
then(argument("rate", floatArg(0.1F, 500.0F)).
|
||||
suggests( (c, b) -> suggestMatching(new String[]{"20.0"},b)).
|
||||
executes((c) -> setTps(c.getSource(), getFloat(c, "rate"))))).
|
||||
then(literal("warp").
|
||||
executes( (c)-> setWarp(c.getSource(), 0, null)).
|
||||
then(argument("ticks", integer(0,4000000)).
|
||||
suggests( (c, b) -> suggestMatching(new String[]{"3600","72000"},b)).
|
||||
executes((c) -> setWarp(c.getSource(), getInteger(c,"ticks"), null)).
|
||||
then(argument("tail command", greedyString()).
|
||||
executes( (c) -> setWarp(
|
||||
c.getSource(),
|
||||
getInteger(c,"ticks"),
|
||||
getString(c, "tail command")))))).
|
||||
then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource()))).
|
||||
then(literal("step").
|
||||
executes((c) -> step(1)).
|
||||
then(argument("ticks", integer(1,72000)).
|
||||
suggests( (c, b) -> suggestMatching(new String[]{"20"},b)).
|
||||
executes((c) -> step(getInteger(c,"ticks"))))).
|
||||
then(literal("superHot").executes( (c)-> toggleSuperHot(c.getSource()))).
|
||||
then(literal("health").
|
||||
executes( (c) -> healthReport(c.getSource(), 100)).
|
||||
then(argument("ticks", integer(20,24000)).
|
||||
executes( (c) -> healthReport(c.getSource(), getInteger(c, "ticks"))))).
|
||||
then(literal("entities").
|
||||
executes((c) -> healthEntities(c.getSource(), 100)).
|
||||
then(argument("ticks", integer(20,24000)).
|
||||
executes((c) -> healthEntities(c.getSource(), getInteger(c, "ticks")))));
|
||||
|
||||
|
||||
dispatcher.register(literalargumentbuilder);
|
||||
}
|
||||
|
||||
|
||||
private static int setTps(ServerCommandSource source, float tps)
|
||||
{
|
||||
TickSpeed.tickrate(tps);
|
||||
queryTps(source);
|
||||
return (int)tps;
|
||||
}
|
||||
|
||||
private static int queryTps(ServerCommandSource source)
|
||||
{
|
||||
Messenger.m(source, "w Current tps is: ",String.format("wb %.1f", TickSpeed.tickrate));
|
||||
return (int)TickSpeed.tickrate;
|
||||
}
|
||||
|
||||
private static int setWarp(ServerCommandSource source, int advance, String tail_command)
|
||||
{
|
||||
PlayerEntity player = null;
|
||||
try
|
||||
{
|
||||
player = source.getPlayer();
|
||||
}
|
||||
catch (CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
BaseComponent message = TickSpeed.tickrate_advance(player, advance, tail_command, source);
|
||||
if (message != null)
|
||||
{
|
||||
source.sendFeedback(message, false);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int toggleFreeze(ServerCommandSource source)
|
||||
{
|
||||
TickSpeed.is_paused = !TickSpeed.is_paused;
|
||||
if (TickSpeed.is_paused)
|
||||
{
|
||||
Messenger.m(source, "gi Game is paused");
|
||||
}
|
||||
else
|
||||
{
|
||||
Messenger.m(source, "gi Game runs normally");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int step(int advance)
|
||||
{
|
||||
TickSpeed.add_ticks_to_run_in_pause(advance);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int toggleSuperHot(ServerCommandSource source)
|
||||
{
|
||||
TickSpeed.is_superHot = !TickSpeed.is_superHot;
|
||||
if (TickSpeed.is_superHot)
|
||||
{
|
||||
Messenger.m(source,"gi Superhot enabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
Messenger.m(source, "gi Superhot disabled");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int healthReport(ServerCommandSource source, int ticks)
|
||||
{
|
||||
CarpetProfiler.prepare_tick_report(ticks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int healthEntities(ServerCommandSource source, int ticks)
|
||||
{
|
||||
CarpetProfiler.prepare_entity_report(ticks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package carpet.fakes;
|
||||
|
||||
public interface ExperienceOrbInterface
|
||||
{
|
||||
int getCombineDelay();
|
||||
void setCombineDelay(int what);
|
||||
void setAmount(int what);
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.TrapdoorBlock;
|
||||
import net.minecraft.dispenser.IBlockSource;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.BlockItemUseContext;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.properties.ComparatorMode;
|
||||
import net.minecraft.state.properties.Half;
|
||||
import net.minecraft.state.properties.SlabType;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.Rotation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BlockRotator
|
||||
{
|
||||
public static boolean flipBlockWithCactus(World worldIn, BlockPos pos, BlockState state, PlayerEntity playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
|
||||
{
|
||||
if (!playerIn.abilities.allowModifyWorld || !CarpetSettings.getBool("flippinCactus") || !player_holds_cactus_mainhand(playerIn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return flip_block(worldIn, pos, state, playerIn, hand, facing, hitX, hitY, hitZ);
|
||||
}
|
||||
|
||||
public static BlockState alternativeBlockPlacement(Block block, BlockItemUseContext context)//World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer)
|
||||
{
|
||||
//actual alternative block placement code
|
||||
//
|
||||
EnumFacing facing;
|
||||
float hitX = context.getHitX();
|
||||
PlayerEntity placer = context.getPlayer();
|
||||
BlockPos pos = context.getPos();
|
||||
World world = context.getWorld();
|
||||
|
||||
if (block instanceof BlockGlazedTerracotta)
|
||||
{
|
||||
facing = EnumFacing.byIndex((int)hitX - 2);
|
||||
if(facing == EnumFacing.UP || facing == EnumFacing.DOWN)
|
||||
{
|
||||
facing = placer.getHorizontalFacing().getOpposite();
|
||||
}
|
||||
return block.getDefaultState().with(BlockHorizontal.HORIZONTAL_FACING, facing);
|
||||
}
|
||||
else if (block instanceof BlockObserver)
|
||||
{
|
||||
return block.getDefaultState()
|
||||
.with(BlockDirectional.FACING, EnumFacing.byIndex((int)hitX - 2))
|
||||
.with(BlockObserver.POWERED, true);
|
||||
}
|
||||
else if (block instanceof BlockRedstoneRepeater)
|
||||
{
|
||||
facing = EnumFacing.byIndex((((int)hitX) % 10) - 2);
|
||||
if(facing == EnumFacing.UP || facing == EnumFacing.DOWN)
|
||||
{
|
||||
facing = placer.getHorizontalFacing().getOpposite();
|
||||
}
|
||||
return block.getDefaultState()
|
||||
.with(BlockHorizontal.HORIZONTAL_FACING, facing)
|
||||
.with(BlockRedstoneRepeater.DELAY, MathHelper.clamp((((int) hitX) / 10) + 1, 1, 4))
|
||||
.with(BlockRedstoneRepeater.LOCKED, Boolean.FALSE);
|
||||
}
|
||||
else if (block instanceof BlockTrapDoor)
|
||||
{
|
||||
return block.getDefaultState()
|
||||
.with(TrapdoorBlock.HORIZONTAL_FACING, EnumFacing.byIndex((((int)hitX) % 10) - 2))
|
||||
.with(TrapdoorBlock.OPEN, Boolean.FALSE)
|
||||
.with(TrapdoorBlock.HALF, (hitX > 10) ? Half.TOP : Half.BOTTOM)
|
||||
.with(TrapdoorBlock.OPEN, world.isBlockPowered(pos));
|
||||
}
|
||||
else if (block instanceof BlockRedstoneComparator)
|
||||
{
|
||||
facing = EnumFacing.byIndex((((int)hitX) % 10) - 2);
|
||||
if((facing == EnumFacing.UP) || (facing == EnumFacing.DOWN))
|
||||
{
|
||||
facing = placer.getHorizontalFacing().getOpposite();
|
||||
}
|
||||
ComparatorMode m = (hitX > 10)?ComparatorMode.SUBTRACT: ComparatorMode.COMPARE;
|
||||
return block.getDefaultState()
|
||||
.with(BlockHorizontal.HORIZONTAL_FACING, facing)
|
||||
.with(BlockRedstoneComparator.POWERED, Boolean.FALSE)
|
||||
.with(BlockRedstoneComparator.MODE, m);
|
||||
}
|
||||
else if (block instanceof BlockDispenser)
|
||||
{
|
||||
return block.getDefaultState()
|
||||
.with(BlockDispenser.FACING, EnumFacing.byIndex((int)hitX - 2))
|
||||
.with(BlockDispenser.TRIGGERED, Boolean.FALSE);
|
||||
}
|
||||
else if (block instanceof BlockPistonBase)
|
||||
{
|
||||
return block.getDefaultState()
|
||||
.with(BlockDirectional.FACING,EnumFacing.byIndex((int)hitX - 2) )
|
||||
.with(BlockPistonBase.EXTENDED, Boolean.FALSE);
|
||||
}
|
||||
else if (block instanceof BlockStairs)
|
||||
{
|
||||
return block.getStateForPlacement(context)//worldIn, pos, facing, hitX, hitY, hitZ, meta, placer)
|
||||
.with(BlockStairs.FACING, EnumFacing.byIndex((((int)hitX) % 10) - 2))
|
||||
.with(BlockStairs.HALF, ( hitX > 10)?Half.TOP : Half.BOTTOM);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ItemStack dispenserRotate(IBlockSource source, ItemStack stack)
|
||||
{
|
||||
EnumFacing sourceFace = source.getBlockState().get(BlockDispenser.FACING);
|
||||
World world = source.getWorld();
|
||||
BlockPos blockpos = source.getBlockPos().offset(sourceFace);
|
||||
BlockState iblockstate = world.getBlockState(blockpos);
|
||||
Block block = iblockstate.getBlock();
|
||||
|
||||
// Block rotation for blocks that can be placed in all 6 rotations.
|
||||
if(block instanceof BlockDirectional || block instanceof BlockDispenser)
|
||||
{
|
||||
EnumFacing face = iblockstate.get(BlockDirectional.FACING);
|
||||
face = face.rotateAround(sourceFace.getAxis());
|
||||
if(sourceFace.getIndex() % 2 == 0)
|
||||
{ // Rotate twice more to make blocks always rotate clockwise relative to the dispenser
|
||||
// when index is equal to zero. when index is equal to zero the dispenser is in the opposite direction.
|
||||
face = face.rotateAround(sourceFace.getAxis());
|
||||
face = face.rotateAround(sourceFace.getAxis());
|
||||
}
|
||||
world.setBlockState(blockpos, iblockstate.with(BlockDirectional.FACING, face), 3);
|
||||
|
||||
}
|
||||
else if(block instanceof BlockHorizontal) // Block rotation for blocks that can be placed in only 4 horizontal rotations.
|
||||
{
|
||||
EnumFacing face = iblockstate.get(BlockHorizontal.HORIZONTAL_FACING);
|
||||
face = face.rotateAround(sourceFace.getAxis());
|
||||
if(sourceFace.getIndex() % 2 == 0)
|
||||
{ // same as above.
|
||||
face = face.rotateAround(sourceFace.getAxis());
|
||||
face = face.rotateAround(sourceFace.getAxis());
|
||||
}
|
||||
if(sourceFace.getIndex() <= 1)
|
||||
{ // Make sure to suppress rotation when index is lower then 2 as that will result in a faulty rotation for
|
||||
// blocks that only can be placed horizontaly.
|
||||
world.setBlockState(blockpos, iblockstate.with(BlockHorizontal.HORIZONTAL_FACING, face), 3);
|
||||
}
|
||||
}
|
||||
// Send block update to the block that just have been rotated.
|
||||
world.neighborChanged(blockpos, block, source.getBlockPos());
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static boolean flip_block(World worldIn, BlockPos pos, BlockState state, PlayerEntity playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
|
||||
{
|
||||
Block block = state.getBlock();
|
||||
if ( (block instanceof BlockGlazedTerracotta) || (block instanceof BlockRedstoneDiode) || (block instanceof BlockRailBase) ||
|
||||
(block instanceof BlockTrapDoor) || (block instanceof BlockLever) || (block instanceof BlockFenceGate))
|
||||
{
|
||||
worldIn.setBlockState(pos, block.rotate(state, Rotation.CLOCKWISE_90), 2 | 1024);
|
||||
}
|
||||
else if ((block instanceof BlockObserver) || (block instanceof BlockEndRod))
|
||||
{
|
||||
worldIn.setBlockState(pos, state.with(BlockDirectional.FACING, (EnumFacing)state.get(BlockDirectional.FACING).getOpposite()), 2 | 1024);
|
||||
}
|
||||
else if (block instanceof BlockDispenser)
|
||||
{
|
||||
worldIn.setBlockState(pos, state.with(BlockDispenser.FACING, state.get(BlockDispenser.FACING).getOpposite()), 2 | 1024);
|
||||
}
|
||||
else if (block instanceof BlockPistonBase)
|
||||
{
|
||||
if (!(state.get(BlockPistonBase.EXTENDED)))
|
||||
worldIn.setBlockState(pos, state.with(BlockDirectional.FACING, state.get(BlockDirectional.FACING).getOpposite()), 2 | 1024);
|
||||
}
|
||||
else if (block instanceof BlockSlab)
|
||||
{
|
||||
if (!((BlockSlab) block).isFullCube(state))
|
||||
{
|
||||
worldIn.setBlockState(pos, state.with(BlockSlab.TYPE, state.get(BlockSlab.TYPE) == SlabType.TOP ? SlabType.BOTTOM : SlabType.TOP), 2 | 1024);
|
||||
}
|
||||
}
|
||||
else if (block instanceof BlockHopper)
|
||||
{
|
||||
if ((EnumFacing)state.get(BlockHopper.FACING) != EnumFacing.DOWN)
|
||||
{
|
||||
worldIn.setBlockState(pos, state.with(BlockHopper.FACING, state.get(BlockHopper.FACING).rotateY()), 2 | 1024);
|
||||
}
|
||||
}
|
||||
else if (block instanceof BlockStairs)
|
||||
{
|
||||
//LOG.error(String.format("hit with facing: %s, at side %.1fX, X %.1fY, Y %.1fZ",facing, hitX, hitY, hitZ));
|
||||
if ((facing == EnumFacing.UP && hitY == 1.0f) || (facing == EnumFacing.DOWN && hitY == 0.0f))
|
||||
{
|
||||
worldIn.setBlockState(pos, state.with(BlockStairs.HALF, state.get(BlockStairs.HALF) == Half.TOP ? Half.BOTTOM : Half.TOP ), 2 | 1024);
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean turn_right;
|
||||
if (facing == EnumFacing.NORTH)
|
||||
{
|
||||
turn_right = (hitX <= 0.5);
|
||||
}
|
||||
else if (facing == EnumFacing.SOUTH)
|
||||
{
|
||||
turn_right = !(hitX <= 0.5);
|
||||
}
|
||||
else if (facing == EnumFacing.EAST)
|
||||
{
|
||||
turn_right = (hitZ <= 0.5);
|
||||
}
|
||||
else if (facing == EnumFacing.WEST)
|
||||
{
|
||||
turn_right = !(hitZ <= 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (turn_right)
|
||||
{
|
||||
worldIn.setBlockState(pos, block.rotate(state, Rotation.COUNTERCLOCKWISE_90), 2 | 1024);
|
||||
}
|
||||
else
|
||||
{
|
||||
worldIn.setBlockState(pos, block.rotate(state, Rotation.CLOCKWISE_90), 2 | 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
worldIn.markBlockRangeForRenderUpdate(pos, pos);
|
||||
return true;
|
||||
}
|
||||
private static boolean player_holds_cactus_mainhand(PlayerEntity playerIn)
|
||||
{
|
||||
return (!playerIn.getHeldItemMainhand().isEmpty()
|
||||
&& playerIn.getHeldItemMainhand().getItem() instanceof ItemBlock &&
|
||||
((ItemBlock) (playerIn.getHeldItemMainhand().getItem())).getBlock() == Blocks.CACTUS);
|
||||
}
|
||||
public static boolean flippinEligibility(Entity entity)
|
||||
{
|
||||
if (CarpetSettings.getBool("flippinCactus")
|
||||
&& (entity instanceof PlayerEntity))
|
||||
{
|
||||
PlayerEntity player = (PlayerEntity)entity;
|
||||
return (!player.getHeldItemOffhand().isEmpty()
|
||||
&& player.getHeldItemOffhand().getItem() instanceof ItemBlock &&
|
||||
((ItemBlock) (player.getHeldItemOffhand().getItem())).getBlock() == Blocks.CACTUS);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import net.minecraft.block.Material;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IWorld;
|
||||
|
||||
public class BlockSaplingHelper
|
||||
{
|
||||
// Added code for checking water for dead shrub rule
|
||||
public static boolean hasWater(IWorld worldIn, BlockPos pos)
|
||||
{
|
||||
for (BlockPos blockpos$mutableblockpos : BlockPos.iterate(pos.add(-4, -4, -4), pos.add(4, 1, 4)))
|
||||
{
|
||||
if (worldIn.getBlockState(blockpos$mutableblockpos).getMaterial() == Material.WATER)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CtrlQCrafting {
|
||||
|
||||
public static ItemStack dropAllCrafting(EntityPlayer playerIn, int index, List<Slot> invSlotParam)
|
||||
{
|
||||
ItemStack itemstack = ItemStack.EMPTY;
|
||||
Slot slot = invSlotParam.get(index);
|
||||
|
||||
if (slot != null && slot.getHasStack())
|
||||
{
|
||||
ItemStack itemstack1 = slot.getStack();
|
||||
itemstack = itemstack1.copy();
|
||||
EntityEquipmentSlot entityequipmentslot = EntityLiving.getSlotForItemStack(itemstack);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
playerIn.dropItem(itemstack, true);
|
||||
|
||||
itemstack1.setCount(0);
|
||||
|
||||
slot.onSlotChange(itemstack1, itemstack);
|
||||
}
|
||||
|
||||
if (itemstack.getCount() == itemstack1.getCount())
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemstack2 = slot.onTake(playerIn, itemstack1);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
playerIn.dropItem(itemstack2, false);
|
||||
}
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,865 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import com.google.common.base.Predicate;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockCommandBlock;
|
||||
import net.minecraft.block.BlockStructure;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.BlockWorldState;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.fluid.IFluidState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemSword;
|
||||
import net.minecraft.network.play.client.CPacketPlayerDigging;
|
||||
import net.minecraft.util.EntitySelectors;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RayTraceFluidMode;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.GameType;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityPlayerActionPack
|
||||
{
|
||||
private EntityPlayerMP player;
|
||||
|
||||
private boolean doesAttack;
|
||||
private int attackInterval;
|
||||
private int attackCooldown;
|
||||
|
||||
private boolean doesUse;
|
||||
private int useInterval;
|
||||
private int useCooldown;
|
||||
|
||||
private boolean doesJump;
|
||||
private int jumpInterval;
|
||||
private int jumpCooldown;
|
||||
|
||||
private BlockPos currentBlock = new BlockPos(-1,-1,-1);
|
||||
private int blockHitDelay;
|
||||
private boolean isHittingBlock;
|
||||
private float curBlockDamageMP;
|
||||
|
||||
private boolean sneaking;
|
||||
private boolean sprinting;
|
||||
private float forward;
|
||||
private float strafing;
|
||||
|
||||
public EntityPlayerActionPack(EntityPlayerMP playerIn)
|
||||
{
|
||||
player = playerIn;
|
||||
stop();
|
||||
}
|
||||
public void copyFrom(EntityPlayerActionPack other)
|
||||
{
|
||||
doesAttack = other.doesAttack;
|
||||
attackInterval = other.attackInterval;
|
||||
attackCooldown = other.attackCooldown;
|
||||
|
||||
doesUse = other.doesUse;
|
||||
useInterval = other.useInterval;
|
||||
useCooldown = other.useCooldown;
|
||||
|
||||
doesJump = other.doesJump;
|
||||
jumpInterval = other.jumpInterval;
|
||||
jumpCooldown = other.jumpCooldown;
|
||||
|
||||
|
||||
currentBlock = other.currentBlock;
|
||||
blockHitDelay = other.blockHitDelay;
|
||||
isHittingBlock = other.isHittingBlock;
|
||||
curBlockDamageMP = other.curBlockDamageMP;
|
||||
|
||||
sneaking = other.sneaking;
|
||||
sprinting = other.sprinting;
|
||||
forward = other.forward;
|
||||
strafing = other.strafing;
|
||||
}
|
||||
|
||||
public EntityPlayerActionPack setAttack(int interval, int offset)
|
||||
{
|
||||
if (interval < 1)
|
||||
{
|
||||
CarpetSettings.LOG.error("attack interval needs to be positive");
|
||||
return this;
|
||||
}
|
||||
this.doesAttack = true;
|
||||
this.attackInterval = interval;
|
||||
this.attackCooldown = interval+offset;
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setUse(int interval, int offset)
|
||||
{
|
||||
if (interval < 1)
|
||||
{
|
||||
CarpetSettings.LOG.error("use interval needs to be positive");
|
||||
return this;
|
||||
}
|
||||
this.doesUse = true;
|
||||
this.useInterval = interval;
|
||||
this.useCooldown = interval+offset;
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setUseForever()
|
||||
{
|
||||
this.doesUse = true;
|
||||
this.useInterval = 1;
|
||||
this.useCooldown = 1;
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setAttackForever()
|
||||
{
|
||||
this.doesAttack = true;
|
||||
this.attackInterval = 1;
|
||||
this.attackCooldown = 1;
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setJump(int interval, int offset)
|
||||
{
|
||||
if (interval < 1)
|
||||
{
|
||||
CarpetSettings.LOG.error("jump interval needs to be positive");
|
||||
return this;
|
||||
}
|
||||
this.doesJump = true;
|
||||
this.jumpInterval = interval;
|
||||
this.jumpCooldown = interval+offset;
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setJumpForever()
|
||||
{
|
||||
this.doesJump = true;
|
||||
this.jumpInterval = 1;
|
||||
this.jumpCooldown = 1;
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setSneaking(boolean doSneak)
|
||||
{
|
||||
sneaking = doSneak;
|
||||
player.setSneaking(doSneak);
|
||||
if (sprinting && sneaking)
|
||||
setSprinting(false);
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setSprinting(boolean doSprint)
|
||||
{
|
||||
sprinting = doSprint;
|
||||
player.setSprinting(doSprint);
|
||||
if (sneaking && sprinting)
|
||||
setSneaking(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityPlayerActionPack setForward(float value)
|
||||
{
|
||||
forward = value;
|
||||
return this;
|
||||
}
|
||||
public EntityPlayerActionPack setStrafing(float value)
|
||||
{
|
||||
strafing = value;
|
||||
return this;
|
||||
}
|
||||
public boolean look(String where)
|
||||
{
|
||||
switch (where)
|
||||
{
|
||||
case "north":
|
||||
look(180.0f,0.0F); return true;
|
||||
case "south":
|
||||
look (0.0F, 0.0F); return true;
|
||||
case "east":
|
||||
look(-90.0F, 0.0F); return true;
|
||||
case "west":
|
||||
look(90.0F, 0.0F); return true;
|
||||
case "up":
|
||||
look(player.rotationYaw, -90.0F); return true;
|
||||
case "down":
|
||||
look(player.rotationYaw, 90.0F); return true;
|
||||
case "left":
|
||||
case "right":
|
||||
return turn(where);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public EntityPlayerActionPack look(float yaw, float pitch)
|
||||
{
|
||||
player.setPositionAndRotation(player.posX, player.posY, player.posZ, yaw, MathHelper.clamp(pitch,-90.0F, 90.0F));
|
||||
return this;
|
||||
}
|
||||
public boolean turn(String where)
|
||||
{
|
||||
switch (where)
|
||||
{
|
||||
case "left":
|
||||
turn(-90.0F,0.0F); return true;
|
||||
case "right":
|
||||
turn (90.0F, 0.0F); return true;
|
||||
case "back":
|
||||
turn (180.0F, 0.0F); return true;
|
||||
case "up":
|
||||
turn(0.0F, -5.0F); return true;
|
||||
case "down":
|
||||
turn(0.0F, 5.0F); return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public EntityPlayerActionPack turn(float yaw, float pitch)
|
||||
{
|
||||
player.setPositionAndRotation(player.posX, player.posY, player.posZ, player.rotationYaw+yaw,MathHelper.clamp(player.rotationPitch+pitch,-90.0F, 90.0F));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public EntityPlayerActionPack stop()
|
||||
{
|
||||
this.doesUse = false;
|
||||
this.doesAttack = false;
|
||||
this.doesJump = false;
|
||||
resetBlockRemoving(false);
|
||||
setSneaking(false);
|
||||
setSprinting(false);
|
||||
forward = 0.0F;
|
||||
strafing = 0.0F;
|
||||
player.setJumping(false);
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void swapHands()
|
||||
{
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(null, null, CPacketPlayerDigging.Action.SWAP_HELD_ITEMS));
|
||||
}
|
||||
|
||||
public void dropItem()
|
||||
{
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(null, null, CPacketPlayerDigging.Action.DROP_ITEM));
|
||||
}
|
||||
public void mount()
|
||||
{
|
||||
List<Entity> entities = player.world.getEntitiesWithinAABBExcludingEntity(player,player.getBoundingBox().expand(3.0D, 1.0D, 3.0D));
|
||||
if (entities.size()==0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Entity closest = entities.get(0);
|
||||
double distance = player.getDistanceSq(closest);
|
||||
for (Entity e: entities)
|
||||
{
|
||||
double dd = player.getDistanceSq(e);
|
||||
if (dd<distance)
|
||||
{
|
||||
distance = dd;
|
||||
closest = e;
|
||||
}
|
||||
}
|
||||
player.startRiding(closest,true);
|
||||
}
|
||||
public void dismount()
|
||||
{
|
||||
player.stopRiding();
|
||||
}
|
||||
|
||||
public void onUpdate()
|
||||
{
|
||||
if (doesJump)
|
||||
{
|
||||
if (--jumpCooldown==0)
|
||||
{
|
||||
jumpCooldown = jumpInterval;
|
||||
//jumpOnce();
|
||||
player.setJumping(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
player.setJumping(false);
|
||||
}
|
||||
}
|
||||
|
||||
boolean used = false;
|
||||
|
||||
if (doesUse && (--useCooldown)==0)
|
||||
{
|
||||
useCooldown = useInterval;
|
||||
used = useOnce();
|
||||
}
|
||||
if (doesAttack)
|
||||
{
|
||||
if ((--attackCooldown) == 0)
|
||||
{
|
||||
attackCooldown = attackInterval;
|
||||
if (!(used)) attackOnce();
|
||||
}
|
||||
}
|
||||
if (forward != 0.0F)
|
||||
{
|
||||
player.moveForward = forward*(sneaking?0.3F:1.0F);
|
||||
}
|
||||
if (strafing != 0.0F)
|
||||
{
|
||||
player.moveStrafing = strafing*(sneaking?0.3F:1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
public void jumpOnce()
|
||||
{
|
||||
if (player.onGround)
|
||||
{
|
||||
player.jump();
|
||||
}
|
||||
}
|
||||
|
||||
public void attackOnce()
|
||||
{
|
||||
RayTraceResult raytraceresult = mouseOver();
|
||||
if(raytraceresult == null) return;
|
||||
|
||||
switch (raytraceresult.type)
|
||||
{
|
||||
case ENTITY:
|
||||
player.attackTargetEntityWithCurrentItem(raytraceresult.entity);
|
||||
this.player.swingArm(EnumHand.MAIN_HAND);
|
||||
this.player.resetCooldown();
|
||||
break;
|
||||
case MISS:
|
||||
break;
|
||||
case BLOCK:
|
||||
BlockPos blockpos = raytraceresult.getBlockPos();
|
||||
if (player.getEntityWorld().getBlockState(blockpos).getMaterial() != Material.AIR)
|
||||
{
|
||||
onPlayerDamageBlock(blockpos,raytraceresult.sideHit.getOpposite());
|
||||
this.player.swingArm(EnumHand.MAIN_HAND);
|
||||
if (attackInterval > 1)
|
||||
{
|
||||
resetBlockRemoving(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean useOnce()
|
||||
{
|
||||
RayTraceResult raytraceresult = mouseOver();
|
||||
for (EnumHand enumhand : EnumHand.values())
|
||||
{
|
||||
ItemStack itemstack = this.player.getHeldItem(enumhand);
|
||||
if (raytraceresult != null)
|
||||
{
|
||||
switch (raytraceresult.type)
|
||||
{
|
||||
case ENTITY:
|
||||
Entity target = raytraceresult.entity;
|
||||
Vec3d vec3d = new Vec3d(raytraceresult.hitVec.x - target.posX, raytraceresult.hitVec.y - target.posY, raytraceresult.hitVec.z - target.posZ);
|
||||
|
||||
boolean flag = player.canEntityBeSeen(target);
|
||||
double d0 = 36.0D;
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
d0 = 9.0D;
|
||||
}
|
||||
|
||||
if (player.getDistanceSq(target) < d0)
|
||||
{
|
||||
EnumActionResult res = player.interactOn(target,enumhand);
|
||||
if (res == EnumActionResult.SUCCESS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
res = target.applyPlayerInteraction(player, vec3d, enumhand);
|
||||
if (res == EnumActionResult.SUCCESS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MISS:
|
||||
break;
|
||||
case BLOCK:
|
||||
BlockPos blockpos = raytraceresult.getBlockPos();
|
||||
|
||||
if (player.getEntityWorld().getBlockState(blockpos).getMaterial() != Material.AIR)
|
||||
{
|
||||
float x = (float) raytraceresult.hitVec.x;
|
||||
float y = (float) raytraceresult.hitVec.y;
|
||||
float z = (float) raytraceresult.hitVec.z;
|
||||
|
||||
EnumActionResult res = player.interactionManager.processRightClickBlock(player, player.getEntityWorld(), itemstack, enumhand, blockpos, raytraceresult.sideHit, x, y, z);
|
||||
if (res == EnumActionResult.SUCCESS)
|
||||
{
|
||||
this.player.swingArm(enumhand);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EnumActionResult res = player.interactionManager.processRightClick(player,player.getEntityWorld(),itemstack,enumhand);
|
||||
if (res == EnumActionResult.SUCCESS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private RayTraceResult rayTraceBlocks(double blockReachDistance)
|
||||
{
|
||||
Vec3d eyeVec = player.getEyePosition(1.0F);
|
||||
Vec3d lookVec = player.getLook(1.0F);
|
||||
Vec3d pointVec = eyeVec.add(lookVec.x * blockReachDistance, lookVec.y * blockReachDistance, lookVec.z * blockReachDistance);
|
||||
return player.getEntityWorld().rayTraceBlocks(eyeVec, pointVec, RayTraceFluidMode.NEVER, false, true);
|
||||
}
|
||||
|
||||
private RayTraceResult mouseOver()
|
||||
{
|
||||
World world = player.getEntityWorld();
|
||||
RayTraceResult result = null;
|
||||
|
||||
Entity pointedEntity = null;
|
||||
double reach = player.isCreative() ? 5.0D : 4.5D;
|
||||
result = rayTraceBlocks(reach);
|
||||
Vec3d eyeVec = player.getEyePosition(1.0F);
|
||||
boolean flag = !player.isCreative();
|
||||
if (player.isCreative()) reach = 6.0D;
|
||||
double extendedReach = reach;
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
extendedReach = result.hitVec.distanceTo(eyeVec);
|
||||
if (world.getBlockState(result.getBlockPos()).getMaterial() == Material.AIR)
|
||||
result = null;
|
||||
}
|
||||
|
||||
Vec3d lookVec = player.getLook(1.0F);
|
||||
Vec3d pointVec = eyeVec.add(lookVec.x * reach, lookVec.y * reach, lookVec.z * reach);
|
||||
Vec3d hitVec = null;
|
||||
List<Entity> list = world.getEntitiesInAABBexcluding(
|
||||
player,
|
||||
player.getBoundingBox().expand(lookVec.x * reach, lookVec.y * reach, lookVec.z * reach).grow(1.0D, 1.0D, 1.0D),
|
||||
EntitySelectors.NOT_SPECTATING.and((Predicate<Entity>) e -> e != null && e.canBeCollidedWith())
|
||||
);
|
||||
double d2 = extendedReach;
|
||||
|
||||
for (int j = 0; j < list.size(); ++j)
|
||||
{
|
||||
Entity entity1 = list.get(j);
|
||||
AxisAlignedBB axisalignedbb = entity1.getBoundingBox().grow((double) entity1.getCollisionBorderSize());
|
||||
RayTraceResult raytraceresult = axisalignedbb.calculateIntercept(eyeVec, pointVec);
|
||||
|
||||
if (axisalignedbb.contains(eyeVec))
|
||||
{
|
||||
if (d2 >= 0.0D)
|
||||
{
|
||||
pointedEntity = entity1;
|
||||
hitVec = raytraceresult == null ? eyeVec : raytraceresult.hitVec;
|
||||
d2 = 0.0D;
|
||||
}
|
||||
}
|
||||
else if (raytraceresult != null)
|
||||
{
|
||||
double d3 = eyeVec.distanceTo(raytraceresult.hitVec);
|
||||
|
||||
if (d3 < d2 || d2 == 0.0D)
|
||||
{
|
||||
if (entity1.getLowestRidingEntity() == player.getLowestRidingEntity())
|
||||
{
|
||||
if (d2 == 0.0D)
|
||||
{
|
||||
pointedEntity = entity1;
|
||||
hitVec = raytraceresult.hitVec;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pointedEntity = entity1;
|
||||
hitVec = raytraceresult.hitVec;
|
||||
d2 = d3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pointedEntity != null && flag && eyeVec.distanceTo(hitVec) > 3.0D)
|
||||
{
|
||||
pointedEntity = null;
|
||||
result = new RayTraceResult(RayTraceResult.Type.MISS, hitVec, (EnumFacing) null, new BlockPos(hitVec));
|
||||
}
|
||||
|
||||
if (pointedEntity != null && (d2 < extendedReach || result == null))
|
||||
{
|
||||
result = new RayTraceResult(pointedEntity, hitVec);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean clickBlock(BlockPos loc, EnumFacing face) // don't call this one
|
||||
{
|
||||
World world = player.getEntityWorld();
|
||||
if (player.interactionManager.getGameType()!=GameType.ADVENTURE)
|
||||
{
|
||||
if (player.interactionManager.getGameType() == GameType.SPECTATOR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!player.abilities.allowEdit)
|
||||
{
|
||||
ItemStack itemstack = player.getHeldItemMainhand();
|
||||
|
||||
if (itemstack.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockWorldState blockworldstate = new BlockWorldState(world, loc, false);
|
||||
|
||||
if (!itemstack.canDestroy(world.getTags(), blockworldstate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!world.getWorldBorder().contains(loc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player.interactionManager.getGameType()==GameType.CREATIVE)
|
||||
{
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(loc, face, CPacketPlayerDigging.Action.START_DESTROY_BLOCK));
|
||||
clickBlockCreative(world, loc, face);
|
||||
this.blockHitDelay = 5;
|
||||
}
|
||||
else if (!this.isHittingBlock || !(currentBlock.equals(loc)))
|
||||
{
|
||||
if (this.isHittingBlock)
|
||||
{
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(this.currentBlock, face, CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK));
|
||||
}
|
||||
|
||||
IBlockState iblockstate = world.getBlockState(loc);
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(loc, face, CPacketPlayerDigging.Action.START_DESTROY_BLOCK));
|
||||
boolean flag = iblockstate.getMaterial() != Material.AIR;
|
||||
|
||||
if (flag && this.curBlockDamageMP == 0.0F)
|
||||
{
|
||||
iblockstate.getBlock().onBlockClicked(iblockstate, world, loc, player);
|
||||
}
|
||||
|
||||
if (flag && iblockstate.getPlayerRelativeBlockHardness(player, world, loc) >= 1.0F)
|
||||
{
|
||||
this.onPlayerDestroyBlock(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isHittingBlock = true;
|
||||
this.currentBlock = loc;
|
||||
this.curBlockDamageMP = 0.0F;
|
||||
world.sendBlockBreakProgress(player.getEntityId(), this.currentBlock, (int)(this.curBlockDamageMP * 10.0F) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void clickBlockCreative(World world, BlockPos pos, EnumFacing facing)
|
||||
{
|
||||
if (!world.extinguishFire(player, pos, facing))
|
||||
{
|
||||
onPlayerDestroyBlock(pos);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onPlayerDamageBlock(BlockPos posBlock, EnumFacing directionFacing) //continue clicking - one to call
|
||||
{
|
||||
if (this.blockHitDelay > 0)
|
||||
{
|
||||
--this.blockHitDelay;
|
||||
return true;
|
||||
}
|
||||
World world = player.getEntityWorld();
|
||||
if (player.interactionManager.getGameType()==GameType.CREATIVE && world.getWorldBorder().contains(posBlock))
|
||||
{
|
||||
this.blockHitDelay = 5;
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(posBlock, directionFacing, CPacketPlayerDigging.Action.START_DESTROY_BLOCK));
|
||||
clickBlockCreative(world, posBlock, directionFacing);
|
||||
return true;
|
||||
}
|
||||
else if (posBlock.equals(currentBlock))
|
||||
{
|
||||
IBlockState iblockstate = world.getBlockState(posBlock);
|
||||
|
||||
if (iblockstate.getMaterial() == Material.AIR)
|
||||
{
|
||||
this.isHittingBlock = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.curBlockDamageMP += iblockstate.getPlayerRelativeBlockHardness(player, world, posBlock);
|
||||
|
||||
if (this.curBlockDamageMP >= 1.0F)
|
||||
{
|
||||
this.isHittingBlock = false;
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(posBlock, directionFacing, CPacketPlayerDigging.Action.STOP_DESTROY_BLOCK));
|
||||
this.onPlayerDestroyBlock(posBlock);
|
||||
this.curBlockDamageMP = 0.0F;
|
||||
this.blockHitDelay = 5;
|
||||
}
|
||||
//player.getEntityId()
|
||||
//send to all, even the breaker
|
||||
world.sendBlockBreakProgress(-1, this.currentBlock, (int)(this.curBlockDamageMP * 10.0F) - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.clickBlock(posBlock, directionFacing);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onPlayerDestroyBlock(BlockPos pos)
|
||||
{
|
||||
World world = player.getEntityWorld();
|
||||
if (player.interactionManager.getGameType()!=GameType.ADVENTURE)
|
||||
{
|
||||
if (player.interactionManager.getGameType() == GameType.SPECTATOR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.abilities.allowEdit)
|
||||
{
|
||||
ItemStack itemstack = player.getHeldItemMainhand();
|
||||
|
||||
if (itemstack.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockWorldState blockworldstate = new BlockWorldState(world, pos, false);
|
||||
|
||||
if (!itemstack.canDestroy(world.getTags(), blockworldstate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (player.interactionManager.getGameType()==GameType.CREATIVE && !player.getHeldItemMainhand().isEmpty() && player.getHeldItemMainhand().getItem() instanceof ItemSword)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IBlockState iblockstate = world.getBlockState(pos);
|
||||
Block block = iblockstate.getBlock();
|
||||
|
||||
if ((block instanceof BlockCommandBlock || block instanceof BlockStructure) && !player.canUseCommandBlock())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (iblockstate.getMaterial() == Material.AIR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
world.playEvent(2001, pos, Block.getStateId(iblockstate));
|
||||
block.onBlockHarvested(world, pos, iblockstate, player);
|
||||
IFluidState ifluidstate = world.getFluidState(pos);
|
||||
boolean flag = world.setBlockState(pos, ifluidstate.getBlockState(), 11);
|
||||
|
||||
if (flag)
|
||||
{
|
||||
block.onPlayerDestroy(world, pos, iblockstate);
|
||||
}
|
||||
|
||||
this.currentBlock = new BlockPos(this.currentBlock.getX(), -1, this.currentBlock.getZ());
|
||||
|
||||
if (!(player.interactionManager.getGameType()==GameType.CREATIVE))
|
||||
{
|
||||
ItemStack itemstack1 = player.getHeldItemMainhand();
|
||||
|
||||
if (!itemstack1.isEmpty())
|
||||
{
|
||||
itemstack1.onBlockDestroyed(world, iblockstate, pos, player);
|
||||
|
||||
if (itemstack1.isEmpty())
|
||||
{
|
||||
player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetBlockRemoving(boolean force)
|
||||
{
|
||||
if (this.isHittingBlock || force)
|
||||
{
|
||||
player.connection.processPlayerDigging(new CPacketPlayerDigging(this.currentBlock, EnumFacing.DOWN, CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK));
|
||||
this.isHittingBlock = false;
|
||||
this.curBlockDamageMP = 0.0F;
|
||||
player.getEntityWorld().sendBlockBreakProgress(player.getEntityId(), this.currentBlock, -1);
|
||||
player.resetCooldown();
|
||||
blockHitDelay = 0;
|
||||
this.currentBlock = new BlockPos(-1,-1,-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public EnumActionResult processRightClickBlock(EntityPlayerSP player, WorldClient worldIn, BlockPos stack, EnumFacing pos, Vec3d facing, EnumHand vec)
|
||||
{
|
||||
this.syncCurrentPlayItem();
|
||||
ItemStack itemstack = player.getHeldItem(vec);
|
||||
float f = (float)(facing.xCoord - (double)stack.getX());
|
||||
float f1 = (float)(facing.yCoord - (double)stack.getY());
|
||||
float f2 = (float)(facing.zCoord - (double)stack.getZ());
|
||||
boolean flag = false;
|
||||
|
||||
if (!this.mc.world.getWorldBorder().contains(stack))
|
||||
{
|
||||
return EnumActionResult.FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.currentGameType != GameType.SPECTATOR)
|
||||
{
|
||||
IBlockState iblockstate = worldIn.getBlockState(stack);
|
||||
|
||||
if ((!player.isSneaking() || player.getHeldItemMainhand().func_190926_b() && player.getHeldItemOffhand().func_190926_b()) && iblockstate.getBlock().onBlockActivated(worldIn, stack, iblockstate, player, vec, pos, f, f1, f2))
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!flag && itemstack.getItem() instanceof ItemBlock)
|
||||
{
|
||||
ItemBlock itemblock = (ItemBlock)itemstack.getItem();
|
||||
|
||||
if (!itemblock.canPlaceBlockOnSide(worldIn, stack, pos, player, itemstack))
|
||||
{
|
||||
return EnumActionResult.FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.connection.sendPacket(new CPacketPlayerTryUseItemOnBlock(stack, pos, vec, f, f1, f2));
|
||||
|
||||
if (!flag && this.currentGameType != GameType.SPECTATOR)
|
||||
{
|
||||
if (itemstack.func_190926_b())
|
||||
{
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
else if (player.getCooldownTracker().hasCooldown(itemstack.getItem()))
|
||||
{
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (itemstack.getItem() instanceof ItemBlock && !player.canUseCommandBlock())
|
||||
{
|
||||
Block block = ((ItemBlock)itemstack.getItem()).getBlock();
|
||||
|
||||
if (block instanceof BlockCommandBlock || block instanceof BlockStructure)
|
||||
{
|
||||
return EnumActionResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentGameType.isCreative())
|
||||
{
|
||||
int i = itemstack.getMetadata();
|
||||
int j = itemstack.func_190916_E();
|
||||
EnumActionResult enumactionresult = itemstack.onItemUse(player, worldIn, stack, vec, pos, f, f1, f2);
|
||||
itemstack.setItemDamage(i);
|
||||
itemstack.func_190920_e(j);
|
||||
return enumactionresult;
|
||||
}
|
||||
else
|
||||
{
|
||||
return itemstack.onItemUse(player, worldIn, stack, vec, pos, f, f1, f2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return EnumActionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EnumActionResult processRightClick(EntityPlayer player, World worldIn, EnumHand stack)
|
||||
{
|
||||
if (this.currentGameType == GameType.SPECTATOR)
|
||||
{
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.syncCurrentPlayItem();
|
||||
this.connection.sendPacket(new CPacketPlayerTryUseItem(stack));
|
||||
ItemStack itemstack = player.getHeldItem(stack);
|
||||
|
||||
if (player.getCooldownTracker().hasCooldown(itemstack.getItem()))
|
||||
{
|
||||
return EnumActionResult.PASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = itemstack.func_190916_E();
|
||||
ActionResult<ItemStack> actionresult = itemstack.useItemRightClick(worldIn, player, stack);
|
||||
ItemStack itemstack1 = actionresult.getResult();
|
||||
|
||||
if (itemstack1 != itemstack || itemstack1.func_190916_E() != i)
|
||||
{
|
||||
player.setHeldItem(stack, itemstack1);
|
||||
}
|
||||
|
||||
return actionresult.getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EnumActionResult interactWithEntity(EntityPlayer player, Entity target, EnumHand heldItem)
|
||||
{
|
||||
this.syncCurrentPlayItem();
|
||||
this.connection.sendPacket(new CPacketUseEntity(target, heldItem));
|
||||
return this.currentGameType == GameType.SPECTATOR ? EnumActionResult.PASS : player.func_190775_a(target, heldItem);
|
||||
}
|
||||
|
||||
/
|
||||
* Handles right clicking an entity from the entities side, sends a packet to the server.
|
||||
*
|
||||
public EnumActionResult interactWithEntity(EntityPlayer player, Entity target, RayTraceResult raytrace, EnumHand heldItem)
|
||||
{
|
||||
this.syncCurrentPlayItem();
|
||||
Vec3d vec3d = new Vec3d(raytrace.hitVec.xCoord - target.posX, raytrace.hitVec.yCoord - target.posY, raytrace.hitVec.zCoord - target.posZ);
|
||||
this.connection.sendPacket(new CPacketUseEntity(target, heldItem, vec3d));
|
||||
return this.currentGameType == GameType.SPECTATOR ? EnumActionResult.PASS : target.applyPlayerInteraction(player, vec3d, heldItem);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.init.Biomes;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.provider.BiomeProvider;
|
||||
import net.minecraft.world.biome.provider.OverworldBiomeProvider;
|
||||
import net.minecraft.world.biome.provider.OverworldBiomeProviderSettings;
|
||||
import net.minecraft.world.gen.ChunkGeneratorOverworld;
|
||||
import net.minecraft.world.gen.IChunkGenerator;
|
||||
import net.minecraft.world.gen.OverworldGenSettings;
|
||||
import net.minecraft.world.gen.feature.BlockBlobConfig;
|
||||
import net.minecraft.world.gen.feature.Feature;
|
||||
import net.minecraft.world.gen.feature.IFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.IcebergConfig;
|
||||
import net.minecraft.world.gen.feature.LakesConfig;
|
||||
import net.minecraft.world.gen.feature.NoFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.SeaGrassConfig;
|
||||
import net.minecraft.world.gen.feature.ShrubFeature;
|
||||
import net.minecraft.world.gen.feature.TallGrassConfig;
|
||||
import net.minecraft.world.gen.feature.TallGrassFeature;
|
||||
import net.minecraft.world.gen.feature.structure.MineshaftConfig;
|
||||
import net.minecraft.world.gen.feature.structure.MineshaftStructure;
|
||||
import net.minecraft.world.gen.feature.structure.OceanRuinConfig;
|
||||
import net.minecraft.world.gen.feature.structure.OceanRuinStructure;
|
||||
import net.minecraft.world.gen.feature.structure.ShipwreckConfig;
|
||||
import net.minecraft.world.gen.feature.structure.Structure;
|
||||
import net.minecraft.world.gen.feature.structure.VillageConfig;
|
||||
import net.minecraft.world.gen.feature.structure.VillagePieces;
|
||||
import net.minecraft.world.gen.placement.CountConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FeatureGenerator
|
||||
{
|
||||
@FunctionalInterface
|
||||
private interface Thing
|
||||
{
|
||||
Boolean plop(World world, BlockPos pos);
|
||||
}
|
||||
private static Thing simplePlop(Feature<NoFeatureConfig> feature)
|
||||
{
|
||||
return simplePlop(feature, IFeatureConfig.NO_FEATURE_CONFIG);
|
||||
}
|
||||
private static Thing simplePlop(Feature feature, IFeatureConfig config)
|
||||
{
|
||||
return (w, p) -> {
|
||||
CarpetSettings.skipGenerationChecks=true;
|
||||
boolean res = feature.place(w, w.getChunkProvider().getChunkGenerator(), w.rand, p, config);
|
||||
CarpetSettings.skipGenerationChecks=false;
|
||||
return res;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private static Thing spawnCustomStructure(Structure structure, IFeatureConfig config)
|
||||
{
|
||||
return (w, p) -> {
|
||||
IChunkGenerator chunkgen = new ChunkGeneratorOverworld(w, null, new OverworldGenSettings())
|
||||
{
|
||||
@Nullable
|
||||
@Override
|
||||
public IFeatureConfig getStructureConfig(Biome biomeIn, Structure<? extends IFeatureConfig> structureIn)
|
||||
{
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeProvider getBiomeProvider()
|
||||
{
|
||||
return new OverworldBiomeProvider(new OverworldBiomeProviderSettings().setWorldInfo(w.getWorldInfo()))
|
||||
{
|
||||
@Nullable
|
||||
@Override
|
||||
public Biome getBiome(BlockPos pos, @Nullable Biome defaultBiome)
|
||||
{
|
||||
return Biomes.PLAINS;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return structure.plopAnywhere(w, p, chunkgen);
|
||||
};
|
||||
}
|
||||
|
||||
public static Boolean spawn(String name, World world, BlockPos pos)
|
||||
{
|
||||
if (featureMap.containsKey(name))
|
||||
return featureMap.get(name).plop(world, pos);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Map<String, Thing> featureMap = new HashMap<String, Thing>() {{
|
||||
|
||||
|
||||
put("oak", simplePlop(Feature.TREE));
|
||||
put("oak_large", simplePlop(Feature.BIG_TREE));
|
||||
put("birch", simplePlop(Feature.BIRCH_TREE));
|
||||
put("birch_large", simplePlop(Feature.TALL_BIRCH_TREE));
|
||||
put("shrub", simplePlop(Feature.SHRUB));
|
||||
put("shrub_acacia", simplePlop(new ShrubFeature(Blocks.ACACIA_WOOD.getDefaultState(), Blocks.ACACIA_LEAVES.getDefaultState())));
|
||||
put("shrub_birch", simplePlop(new ShrubFeature(Blocks.BIRCH_WOOD.getDefaultState(), Blocks.BIRCH_LEAVES.getDefaultState())));
|
||||
put("shrub_snowy", simplePlop(new ShrubFeature(Blocks.BONE_BLOCK.getDefaultState(), Blocks.COBWEB.getDefaultState())));
|
||||
put("jungle", simplePlop(Feature.JUNGLE_TREE));
|
||||
put("spruce_matchstick", simplePlop(Feature.POINTY_TAIGA_TREE));
|
||||
put("dark_oak", simplePlop(Feature.CANOPY_TREE));
|
||||
put("acacia", simplePlop(Feature.SAVANNA_TREE));
|
||||
put("spruce", simplePlop(Feature.TALL_TAIGA_TREE));
|
||||
put("oak_swamp", simplePlop(Feature.SWAMP_TREE));
|
||||
put("jungle_large", simplePlop(Feature.MESA_JUNGLE));
|
||||
put("spruce_matchstick_large", simplePlop(Feature.MEGA_PINE_TREE_1));
|
||||
put("spruce_large", simplePlop(Feature.MEGA_PINE_TREE_2));
|
||||
put("well", simplePlop(Feature.DESERT_WELLS));
|
||||
put("grass_jungle", simplePlop(Feature.JUNGLE_GRASS));
|
||||
put("fern", simplePlop(Feature.TAIGA_GRASS));
|
||||
put("grass", simplePlop(new TallGrassFeature(), new TallGrassConfig(Blocks.GRASS.getDefaultState())));
|
||||
//put("", simplePlop(Feature.));
|
||||
//put("", simplePlop(Feature.));
|
||||
//put("", simplePlop(Feature.));
|
||||
//put("", simplePlop(Feature.));
|
||||
|
||||
put("cactus", simplePlop(Feature.CACTUS));
|
||||
put("dead_bush", simplePlop(Feature.DEAD_BUSH));
|
||||
put("fossils", simplePlop(Feature.FOSSILS)); // spawn above, spawn invisible
|
||||
put("mushroom_brown", simplePlop(Feature.BIG_BROWN_MUSHROOM));
|
||||
put("mushroom_red", simplePlop(Feature.BIG_RED_MUSHROOM));
|
||||
put("ice_spike", simplePlop(Feature.ICE_SPIKE));
|
||||
put("glowstone", simplePlop(Feature.GLOWSTONE));
|
||||
put("melon", simplePlop(Feature.MELON));
|
||||
put("pumpkin", simplePlop(Feature.PUMPKIN));
|
||||
put("sugarcane", simplePlop(Feature.REED));
|
||||
put("lilypad", simplePlop(Feature.WATERLILY));
|
||||
put("dungeon", simplePlop(Feature.DUNGEONS));
|
||||
put("iceberg", simplePlop(Feature.ICEBERG, new IcebergConfig(Blocks.PACKED_ICE.getDefaultState())));
|
||||
put("iceberg_blue", simplePlop(Feature.ICEBERG, new IcebergConfig(Blocks.BLUE_ICE.getDefaultState())));
|
||||
put("lake", simplePlop(Feature.LAKES, new LakesConfig(Blocks.WATER)));
|
||||
put("lake_lava", simplePlop(Feature.LAKES, new LakesConfig(Blocks.LAVA)));
|
||||
//put("end tower", simplePlop(Feature.END_CRYSTAL_TOWER)); // requires more complex setup
|
||||
put("end_island", simplePlop(Feature.END_ISLAND));
|
||||
put("chorus", simplePlop(Feature.CHORUS_PLANT));
|
||||
put("sea_grass", simplePlop(Feature.SEA_GRASS, new SeaGrassConfig(80, 0.8D)));
|
||||
put("sea_grass_river", simplePlop(Feature.SEA_GRASS, new SeaGrassConfig(48, 0.4D)));
|
||||
put("kelp", simplePlop(Feature.KELP));
|
||||
put("coral_tree", simplePlop(Feature.CORAL_TREE));
|
||||
put("coral_mushroom", simplePlop(Feature.CORAL_MUSHROOM));
|
||||
put("coral_claw", simplePlop(Feature.CORAL_CLAW));
|
||||
put("coral", (w, p) ->
|
||||
w.rand.nextInt(3)!=0?
|
||||
(w.rand.nextInt(2)!=0?
|
||||
simplePlop(Feature.CORAL_MUSHROOM).plop(w, p):
|
||||
simplePlop(Feature.CORAL_TREE).plop(w, p))
|
||||
:simplePlop(Feature.CORAL_CLAW).plop(w, p));
|
||||
put("sea_pickle", simplePlop(Feature.SEA_PICKLE, new CountConfig(20)));
|
||||
put("boulder", simplePlop(Feature.BLOCK_BLOB, new BlockBlobConfig(Blocks.MOSSY_COBBLESTONE, 0)));
|
||||
//structures
|
||||
put("monument", Feature.OCEAN_MONUMENT::plopAnywhere);
|
||||
put("fortress", Feature.FORTRESS::plopAnywhere);
|
||||
put("mansion", Feature.WOODLAND_MANSION::plopAnywhere);
|
||||
put("jungle_temple", Feature.JUNGLE_PYRAMID::plopAnywhere);
|
||||
put("desert_temple", Feature.DESERT_PYRAMID::plopAnywhere);
|
||||
put("end_city", Feature.END_CITY::plopAnywhere);
|
||||
put("igloo", Feature.IGLOO::plopAnywhere);
|
||||
put("shipwreck", spawnCustomStructure(Feature.SHIPWRECK, new ShipwreckConfig(true)));
|
||||
put("shipwreck2", spawnCustomStructure(Feature.SHIPWRECK, new ShipwreckConfig(false)));
|
||||
put("witchhut", Feature.SWAMP_HUT::plopAnywhere);
|
||||
put("stronghold", Feature.STRONGHOLD::plopAnywhere);
|
||||
|
||||
put("oceanruin_small", spawnCustomStructure(Feature.OCEAN_RUIN,
|
||||
new OceanRuinConfig(OceanRuinStructure.Type.COLD, 0.0F, 0.5F)));
|
||||
put("oceanruin_warm_small", spawnCustomStructure(Feature.OCEAN_RUIN,
|
||||
new OceanRuinConfig(OceanRuinStructure.Type.WARM, 0.0F, 0.5F)));
|
||||
put("oceanruin_tall", spawnCustomStructure(Feature.OCEAN_RUIN,
|
||||
new OceanRuinConfig(OceanRuinStructure.Type.COLD, 1.0F, 0.0F)));
|
||||
put("oceanruin_warm_tall", spawnCustomStructure(Feature.OCEAN_RUIN,
|
||||
new OceanRuinConfig(OceanRuinStructure.Type.WARM, 1.0F, 0.0F)));
|
||||
put("oceanruin", spawnCustomStructure(Feature.OCEAN_RUIN,
|
||||
new OceanRuinConfig(OceanRuinStructure.Type.COLD, 1.0F, 1.0F)));
|
||||
put("oceanruin_warm", spawnCustomStructure(Feature.OCEAN_RUIN,
|
||||
new OceanRuinConfig(OceanRuinStructure.Type.WARM, 1.0F, 1.0F)));
|
||||
put("treasure", Feature.BURIED_TREASURE::plopAnywhere);
|
||||
|
||||
|
||||
put("mineshaft", spawnCustomStructure(Feature.MINESHAFT, new MineshaftConfig(0.0, MineshaftStructure.Type.NORMAL)));
|
||||
put("mineshaft_mesa", spawnCustomStructure(Feature.MINESHAFT, new MineshaftConfig(0.0, MineshaftStructure.Type.MESA)));
|
||||
|
||||
put("village", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.OAK)));
|
||||
put("village_desert", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.SANDSTONE)));
|
||||
put("village_savanna", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.ACACIA)));
|
||||
put("village_taiga", spawnCustomStructure(Feature.VILLAGE, new VillageConfig(0,VillagePieces.Type.SPRUCE)));
|
||||
|
||||
}};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
|
||||
public class PortalHelper
|
||||
{
|
||||
public static boolean player_holds_obsidian(EntityPlayer playerIn)
|
||||
{
|
||||
if ( (!playerIn.getHeldItemMainhand().isEmpty()
|
||||
&& playerIn.getHeldItemMainhand().getItem() instanceof ItemBlock &&
|
||||
((ItemBlock)(playerIn.getHeldItemMainhand().getItem())).getBlock() == Blocks.OBSIDIAN ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,922 @@
|
||||
package carpet.helpers;
|
||||
//Author: theosib
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.RedstoneWireBlock;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class RedstoneWireTurbo
|
||||
{
|
||||
/*
|
||||
* This is Helper class for RedstoneWireBlock. It implements a minimially-invasive
|
||||
* bolt-on accelerator that performs a breadth-first search through redstone wire blocks
|
||||
* in order to more efficiently and deterministicly compute new redstone wire power levels
|
||||
* and determine the order in which other blocks should be updated.
|
||||
*
|
||||
* Features:
|
||||
* - Changes to RedstoneWireBlock are very limited, no other classes are affected, and the
|
||||
* choice between old and new redstone wire update algorithms is switchable on-line.
|
||||
* - The vanilla implementation relied on World.notifyNeighborsOfStateChange for redstone
|
||||
* wire blocks to communicate power level changes to each other, generating 36 block
|
||||
* updates per call. This improved implementation propagates power level changes directly
|
||||
* between redstone wire blocks. Redstone wire power levels are therefore computed more quickly,
|
||||
* and block updates are sent only to non-redstone blocks, many of which may perform an
|
||||
* action when informed of a change in redstone power level. (Note: Block updates are not
|
||||
* the same as state changes to redstone wire. Wire block states are updated as soon
|
||||
* as they are computed.)
|
||||
* - Of the 36 block updates generated by a call to World.notifyNeighborsOfStateChange,
|
||||
* 12 of them are obviously redundant (e.g. the west neighbor of the east neighbor).
|
||||
* These are eliminated.
|
||||
* - Updates to redstone wire and other connected blocks are propagated in a breath-first
|
||||
* manner, radiating out from the initial trigger (a block update to a redstone wire
|
||||
* from something other than redstone wire).
|
||||
* - Updates are scheduled both deterministicly and in an intuitive order, addressing bug
|
||||
* MC-11193.
|
||||
* - All redstone behavior that used to be locational now works the same in all locations.
|
||||
* - All behaviors of redstone wire that used to be orientational now work the same in all
|
||||
* orientations, as long as orientation can be determined; random otherwise. Some other
|
||||
* redstone components still update directionally (e.g. switches), and this code can't
|
||||
* compensate for that.
|
||||
* - Information that is otherwise computed over and over again or which is expensive to
|
||||
* to compute is cached for faster lookup. This includes coordinates of block position
|
||||
* neighbors and block states that won't change behind our backs during the execution of
|
||||
* this search algorithm.
|
||||
* - Redundant block updates (both to redstone wire and to other blocks) are heavily
|
||||
* consolidated. For worst-case scenarios (depowering of redstone wire) this results
|
||||
* in a reduction of block updates by as much as 95% (factor of 1/21). Due to overheads,
|
||||
* empirical testing shows a speedup better than 10x. This addresses bug MC-81098.
|
||||
*
|
||||
* Extensive testing has been performed to ensure that existing redstone contraptions still
|
||||
* behave as expected. Results of early testing that identified undesirable behavior changes
|
||||
* were addressed. Additionally, real-time performance testing revealed compute inefficiencies
|
||||
* With earlier implementations of this accelerator. Some compatibility adjustments and
|
||||
* performance optimizations resulted in harmless increases in block updates above the
|
||||
* theoretical minimum.
|
||||
*
|
||||
* Only a single redstone machine was found to break: An instant dropper line hack that
|
||||
* relies on powered rails and quasiconnectivity but doesn't work in all directions. The
|
||||
* replacement is to lay redstone wire directly on top of the dropper line, which now works
|
||||
* reliably in any direction.
|
||||
*
|
||||
* There are numerous other optimization that can be made, but those will be provided later in
|
||||
* separate updates. This version is designed to be minimalistic.
|
||||
*
|
||||
* Many thanks to the following individuals for their help in testing this functionality:
|
||||
* - pokechu22, _MethodZz_, WARBEN, NarcolepticFrog, CommandHelper (nessie), ilmango,
|
||||
* OreoLamp, Xcom6000, tryashtar, RedCMD, Smokey95Dog, EDDxample, Rays Works,
|
||||
* Nodnam, BlockyPlays, Grumm, NeunEinser, HelVince.
|
||||
*/
|
||||
|
||||
|
||||
/* Reference to RedstoneWireBlock object, which uses this accelerator */
|
||||
private final RedstoneWireBlock wire;
|
||||
|
||||
|
||||
/*
|
||||
* Implementation:
|
||||
*
|
||||
* RedstoneWire Blocks are updated in concentric rings or "layers" radiating out from the
|
||||
* initial block update that came from a call to RedstoneWireBlock.neighborChanged().
|
||||
* All nodes put in Layer N are those with Manattan distance N from the trigger
|
||||
* position, reachable through connected redstone wire blocks.
|
||||
*
|
||||
* Layer 0 represents the trigger block position that was input to neighborChanged.
|
||||
* Layer 1 contains the immediate neighbors of that position.
|
||||
* Layer N contains the neighbors of blocks in layer N-1, not including
|
||||
* those in previous layers.
|
||||
*
|
||||
* Layers enforce an update order that is a function of Manhattan distance
|
||||
* from the initial coordinates input to neighborChanged. The same
|
||||
* coordinates may appear in multiple layers, but redundant updates are minimized.
|
||||
* Block updates are sent layer-by-layer. If multiple of a block's neighbors experience
|
||||
* redstone wire changes before its layer is processed, then those updates will be merged.
|
||||
* If a block's update has been sent, but its neighboring redstone changes
|
||||
* after that, then another update will be sent. This preserves compatibility with
|
||||
* machines that rely on zero-tick behavior, except that the new functionality is non-
|
||||
* locational.
|
||||
*
|
||||
* Within each layer, updates are ordered left-to-right relative to the direction of
|
||||
* information flow. This makes the implementation non-orientational. Only when
|
||||
* this direction is ambiguous is randomness applied (intentionally).
|
||||
*/
|
||||
//private final List<List<UpdateNode>> updateLayers = new ArrayList<>();
|
||||
private List<UpdateNode> updateQueue0 = new ArrayList<>();
|
||||
private List<UpdateNode> updateQueue1 = new ArrayList<>();
|
||||
private List<UpdateNode> updateQueue2 = new ArrayList<>();
|
||||
|
||||
|
||||
public RedstoneWireTurbo(RedstoneWireBlock wire) {
|
||||
this.wire = wire;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute neighbors of a block. When a redstone wire value changes, previously it called
|
||||
* World.notifyNeighborsOfStateChange. That lists immediately neighboring blocks in
|
||||
* west, east, down, up, north, south order. For each of those neighbors, their own
|
||||
* neighbors are updated in the same order. This generates 36 updates, but 12 of them are
|
||||
* redundant; for instance the west neighbor of a block's east neighbor.
|
||||
*
|
||||
* Note that this ordering is only used to create the initial list of neighbors. Once
|
||||
* the direction of signal flow is identified, the ordering of updates is completely
|
||||
* reorganized.
|
||||
*/
|
||||
public static BlockPos[] computeAllNeighbors(final BlockPos pos) {
|
||||
final int x = pos.getX();
|
||||
final int y = pos.getY();
|
||||
final int z = pos.getZ();
|
||||
final BlockPos[] n = new BlockPos[24];
|
||||
|
||||
// Immediate neighbors, in the same order as
|
||||
// World.notifyNeighborsOfStateChange, etc.:
|
||||
// west, east, down, up, north, south
|
||||
n[ 0] = new BlockPos(x-1, y , z );
|
||||
n[ 1] = new BlockPos(x+1, y , z );
|
||||
n[ 2] = new BlockPos(x , y-1, z );
|
||||
n[ 3] = new BlockPos(x , y+1, z );
|
||||
n[ 4] = new BlockPos(x , y , z-1);
|
||||
n[ 5] = new BlockPos(x , y , z+1);
|
||||
|
||||
// Neighbors of neighbors, in the same order,
|
||||
// except that duplicates are not included
|
||||
n[ 6] = new BlockPos(x-2, y , z );
|
||||
n[ 7] = new BlockPos(x-1, y-1, z );
|
||||
n[ 8] = new BlockPos(x-1, y+1, z );
|
||||
n[ 9] = new BlockPos(x-1, y , z-1);
|
||||
n[10] = new BlockPos(x-1, y , z+1);
|
||||
n[11] = new BlockPos(x+2, y , z );
|
||||
n[12] = new BlockPos(x+1, y-1, z );
|
||||
n[13] = new BlockPos(x+1, y+1, z );
|
||||
n[14] = new BlockPos(x+1, y , z-1);
|
||||
n[15] = new BlockPos(x+1, y , z+1);
|
||||
n[16] = new BlockPos(x , y-2, z );
|
||||
n[17] = new BlockPos(x , y-1, z-1);
|
||||
n[18] = new BlockPos(x , y-1, z+1);
|
||||
n[19] = new BlockPos(x , y+2, z );
|
||||
n[20] = new BlockPos(x , y+1, z-1);
|
||||
n[21] = new BlockPos(x , y+1, z+1);
|
||||
n[22] = new BlockPos(x , y , z-2);
|
||||
n[23] = new BlockPos(x , y , z+2);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only want redstone wires to update redstone wires that are
|
||||
* immediately adjacent. Some more distant updates can result
|
||||
* in cross-talk that (a) wastes time and (b) can make the update
|
||||
* order unintuitive. Therefore (relative to the neighbor order
|
||||
* computed by computeAllNeighbors), updates are not scheduled
|
||||
* for redstone wire in those non-connecting positions. On the
|
||||
* other hand, updates will always be sent to *other* types of blocks
|
||||
* in any of the 24 neighboring positions.
|
||||
*/
|
||||
private static final boolean[] update_redstone = {
|
||||
true, true, false, false, true, true, // 0 to 5
|
||||
false, true, true, false, false, false, // 6 to 11
|
||||
true, true, false, false, false, true, // 12 to 17
|
||||
true, false, true, true, false, false}; // 18 to 23
|
||||
|
||||
// Internal numbering for cardinal directions
|
||||
private static final int North = 0;
|
||||
private static final int East = 1;
|
||||
private static final int South = 2;
|
||||
private static final int West = 3;
|
||||
|
||||
// Names for debug print statements
|
||||
private static final char dirname[] = {'N', 'E', 'S', 'W'};
|
||||
|
||||
/*
|
||||
* These lookup tables completely remap neighbor positions into a left-to-right
|
||||
* ordering, based on the cardinal direction that is determined to be forward.
|
||||
* See below for more explanation.
|
||||
*/
|
||||
private static final int[] forward_is_north = {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15};
|
||||
private static final int[] forward_is_east = {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10};
|
||||
private static final int[] forward_is_south = {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9};
|
||||
private static final int[] forward_is_west = {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14};
|
||||
|
||||
/* For any orientation, we end up with the update order defined below. This order is relative to any redstone wire block
|
||||
* that is itself having an update computed, and this center position is marked with C.
|
||||
* - The update position marked 0 is computed first, and the one marked 23 is last.
|
||||
* - Forward is determined by the local direction of information flow into position C from prior updates.
|
||||
* - The first updates are scheduled for the four positions below and above C.
|
||||
* - Then updates are scheduled for the four horizontal neighbors of C, followed by the positions below and above those neighbors.
|
||||
* - Finally, updates are scheduled for the remaining positions with Manhattan distance 2 from C (at the same Y coordinate).
|
||||
* - For a given horizontal distance from C, updates are scheduled starting from directly left and stepping clockwise to directly
|
||||
* right. The remaining positions behind C are scheduled counterclockwise so as to maintain the left-to-right ordering.
|
||||
* - If C is in layer N of the update schedule, then all 24 positions may be scheduled for layer N+1. For redstone wire, no
|
||||
* updates are scheduled for positions that cannot directly connect. Additionally, the four positions above and below C
|
||||
* are ALSO scheduled for layer N+2.
|
||||
* - This update order was selected after experimenting with a number of alternative schedules, based on its compatibility
|
||||
* with existing redstone designs and behaviors that were considered to be intuitive by various testers. WARBEN in particular
|
||||
* made some of the most challenging test cases, but the 3-tick clocks (made by RedCMD) were also challenging to fix,
|
||||
* along with the rail-based instant dropper line built by ilmango. Numerous others made test cases as well, including
|
||||
* NarcolepticFrog, nessie, and Pokechu22.
|
||||
*
|
||||
* - The forward direction ls determined locally. So when there are branches in the redstone wire, the left one will get updated
|
||||
* before the right one. Each branch can have its own relative forward direction, resulting in the left side of a left branch
|
||||
* having priority over the right branch of a left branch, which has priority over the left branch of a right branch, followed
|
||||
* by the right branch of a right branch. And so forth. Since redstone power reduces to zero after a path distance of 15,
|
||||
* that imposes a practical limit on the branching. Note that the branching is not tracked explicitly -- relative forward
|
||||
* directions dictate relative sort order, which maintains the proper global ordering. This also makes it unnecessary to be
|
||||
* concerned about branches meeting up with each other.
|
||||
*
|
||||
* ^
|
||||
* |
|
||||
* Forward
|
||||
* <-- Left Right -->
|
||||
*
|
||||
* 18
|
||||
* 10 17 5 19 11
|
||||
* 2 8 0 12 16 4 C 6 20 9 1 13 3
|
||||
* 14 21 7 23 15
|
||||
* Further 22 Further
|
||||
* Down Down Up Up
|
||||
*
|
||||
* Backward
|
||||
* |
|
||||
* V
|
||||
*/
|
||||
|
||||
// This allows the above remapping tables to be looked up by cardial direction index
|
||||
private static final int[][] reordering = {forward_is_north, forward_is_east, forward_is_south, forward_is_west};
|
||||
|
||||
/*
|
||||
* Input: Array of UpdateNode objects in an order corresponding to the positions
|
||||
* computed by computeAllNeighbors above.
|
||||
* Output: Array of UpdateNode objects oriented using the above remapping tables
|
||||
* corresponding to the identified heading (direction of information flow).
|
||||
*/
|
||||
private static void orientNeighbors(final UpdateNode[] src, final UpdateNode[] dst, final int heading) {
|
||||
final int[] re = reordering[heading];
|
||||
for (int i=0; i<24; i++) {
|
||||
dst[i] = src[re[i]];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure to keep track of redstone wire blocks and
|
||||
* neighbors that will receive updates.
|
||||
*/
|
||||
private static class UpdateNode {
|
||||
public static enum Type {
|
||||
UNKNOWN, REDSTONE, OTHER
|
||||
}
|
||||
|
||||
BlockState currentState; // Keep track of redstone wire value
|
||||
UpdateNode[] neighbor_nodes; // References to neighbors (directed graph edges)
|
||||
BlockPos self; // UpdateNode's own position
|
||||
BlockPos parent; // Which block pos spawned/updated this node
|
||||
Type type = Type.UNKNOWN; // unknown, redstone wire, other type of block
|
||||
int layer; // Highest layer this node is scheduled in
|
||||
boolean visited; // To keep track of information flow direction, visited restone wire is marked
|
||||
int xbias, zbias; // Remembers directionality of ancestor nodes; helps eliminate directional ambiguities.
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Keep track of all block positions discovered during search and their current states.
|
||||
* We want to remember one entry for each position.
|
||||
*/
|
||||
private final Map<BlockPos, UpdateNode> nodeCache = new HashMap<>();
|
||||
|
||||
|
||||
/*
|
||||
* For a newly created UpdateNode object, determine what type of block it is.
|
||||
*/
|
||||
private void identifyNode(final World worldIn, final UpdateNode upd1) {
|
||||
final BlockPos pos = upd1.self;
|
||||
final BlockState oldState = worldIn.getBlockState(pos);
|
||||
upd1.currentState = oldState;
|
||||
|
||||
// Some neighbors of redstone wire are other kinds of blocks.
|
||||
// These need to receive block updates to inform them that
|
||||
// redstone wire values have changed.
|
||||
final Block block = oldState.getBlock();
|
||||
if (block != wire) {
|
||||
// Mark this block as not redstone wire and therefore
|
||||
// requiring updates
|
||||
upd1.type = UpdateNode.Type.OTHER;
|
||||
|
||||
// Non-redstone blocks may propagate updates, but those updates
|
||||
// are not handled by this accelerator. Therefore, we do not
|
||||
// expand this position's neighbors.
|
||||
return;
|
||||
}
|
||||
|
||||
// One job of RedstoneWireBlock.neighborChanged is to convert
|
||||
// redstone wires to items if the block beneath was removed.
|
||||
// With this accelerator, RedstoneWireBlock.neighborChanged
|
||||
// is only typically called for a single wire block, while
|
||||
// others are processed internally by the breadth first search
|
||||
// algorithm. To preserve this game behavior, this check must
|
||||
// be replicated here.
|
||||
if (!oldState.isValidPosition(worldIn, pos)) {
|
||||
// Pop off the redstone dust
|
||||
oldState.dropBlockAsItem(worldIn, pos, 0);
|
||||
worldIn.removeBlock(pos);
|
||||
|
||||
// Mark this position as not being redstone wire
|
||||
upd1.type = UpdateNode.Type.OTHER;
|
||||
|
||||
// Note: Sending updates to air blocks leads to an empty method.
|
||||
// Testing shows this to be faster than explicitly avoiding updates to
|
||||
// air blocks.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the above conditions fail, then this is a redstone wire block.
|
||||
upd1.type = UpdateNode.Type.REDSTONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given which redstone wire blocks have been visited and not visited
|
||||
* around the position currently being updated, compute the cardinal
|
||||
* direction that is "forward."
|
||||
*
|
||||
* rx is the forward direction along the West/East axis
|
||||
* rz is the forward direction along the North/South axis
|
||||
*/
|
||||
static private int computeHeading(final int rx, final int rz) {
|
||||
// rx and rz can only take on values -1, 0, and 1, so we can
|
||||
// compute a code number that allows us to use a single switch
|
||||
// to determine the heading.
|
||||
final int code = (rx + 1) + 3*(rz + 1);
|
||||
switch (code) {
|
||||
case 0: {
|
||||
// Both rx and rz are -1 (northwest)
|
||||
// Randomly choose one to be forward.
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j==0) ? North : West;
|
||||
}
|
||||
case 1: {
|
||||
// rx=0, rz=-1
|
||||
// Definitively North
|
||||
return North;
|
||||
}
|
||||
case 2: {
|
||||
// rx=1, rz=-1 (northeast)
|
||||
// Choose randomly between north and east
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j==0) ? North : East;
|
||||
}
|
||||
case 3: {
|
||||
// rx=-1, rz=0
|
||||
// Definitively West
|
||||
return West;
|
||||
}
|
||||
case 4: {
|
||||
// rx=0, rz=0
|
||||
// Heading is completely ambiguous. Choose
|
||||
// randomly among the four cardinal directions.
|
||||
return ThreadLocalRandom.current().nextInt(0, 4);
|
||||
}
|
||||
case 5: {
|
||||
// rx=1, rz=0
|
||||
// Definitively East
|
||||
return East;
|
||||
}
|
||||
case 6: {
|
||||
// rx=-1, rz=1 (southwest)
|
||||
// Choose randomly between south and west
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j==0) ? South : West;
|
||||
}
|
||||
case 7: {
|
||||
// rx=0, rz=1
|
||||
// Definitively South
|
||||
return South;
|
||||
}
|
||||
case 8: {
|
||||
// rx=1, rz=1 (southeast)
|
||||
// Choose randomly between south and east
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j==0) ? South : East;
|
||||
}
|
||||
}
|
||||
|
||||
// We should never get here
|
||||
return ThreadLocalRandom.current().nextInt(0, 4);
|
||||
}
|
||||
|
||||
// Select whether to use updateSurroundingRedstone from RedstoneWireBlock (old)
|
||||
// or this helper class (new)
|
||||
private static final boolean old_current_change = false;
|
||||
|
||||
/*
|
||||
* Process a node whose neighboring redstone wire has experienced value changes.
|
||||
*/
|
||||
private void updateNode(final World worldIn, final UpdateNode upd1, final int layer) {
|
||||
final BlockPos pos = upd1.self;
|
||||
|
||||
// Mark this redstone wire as having been visited so that it can be used
|
||||
// to calculate direction of information flow.
|
||||
upd1.visited = true;
|
||||
|
||||
// Look up the last known state.
|
||||
// Due to the way other redstone components are updated, we do not
|
||||
// have to worry about a state changing behind our backs. The rare
|
||||
// exception is handled by scheduleReentrantNeighborChanged.
|
||||
final BlockState oldState = upd1.currentState;
|
||||
|
||||
// Ask the wire block to compute its power level from its neighbors.
|
||||
// This will also update the wire's power level and return a new
|
||||
// state if it has changed. When a wire power level is changed,
|
||||
// calculateCurrentChanges will immediately update the block state in the world
|
||||
// and return the same value here to be cached in the corresponding
|
||||
// UpdateNode object.
|
||||
BlockState newState;
|
||||
if (old_current_change) {
|
||||
newState = wire.func_212568_b(worldIn, pos, oldState);
|
||||
} else {
|
||||
// Looking up block state is slow. This accelerator includes a version of
|
||||
// calculateCurrentChanges that uses cahed wire values for a
|
||||
// significant performance boost.
|
||||
newState = this.calculateCurrentChanges(worldIn, upd1);
|
||||
}
|
||||
|
||||
// Only inform neighors if the state has changed
|
||||
if (newState != oldState) {
|
||||
// Store the new state
|
||||
upd1.currentState = newState;
|
||||
|
||||
// Inform neighbors of the change
|
||||
propagateChanges(worldIn, upd1, layer);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This identifies the neighboring positions of a new UpdateNode object,
|
||||
* determines their types, and links those to into the graph. Then based on
|
||||
* what nodes in the redstone wire graph have been visited, the neighbors
|
||||
* are reordered left-to-right relative to the direction of information flow.
|
||||
*/
|
||||
private void findNeighbors(final World worldIn, final UpdateNode upd1) {
|
||||
final BlockPos pos = upd1.self;
|
||||
|
||||
// Get the list of neighbor coordinates
|
||||
final BlockPos[] neighbors = computeAllNeighbors(pos);
|
||||
|
||||
// Temporary array of neighbors in cardinal ordering
|
||||
final UpdateNode[] neighbor_nodes = new UpdateNode[24];
|
||||
|
||||
// Target array of neighbors sorted left-to-right
|
||||
upd1.neighbor_nodes = new UpdateNode[24];
|
||||
|
||||
for (int i=0; i<24; i++) {
|
||||
// Look up each neighbor in the node cache
|
||||
final BlockPos pos2 = neighbors[i];
|
||||
UpdateNode upd2 = nodeCache.get(pos2);
|
||||
if (upd2 == null) {
|
||||
// If this is a previously unreached position, create
|
||||
// a new update node, add it to the cache, and identify what it is.
|
||||
upd2 = new UpdateNode();
|
||||
upd2.self = pos2;
|
||||
upd2.parent = pos;
|
||||
nodeCache.put(pos2, upd2);
|
||||
identifyNode(worldIn, upd2);
|
||||
}
|
||||
|
||||
// For non-redstone blocks, any of the 24 neighboring positions
|
||||
// should receive a block update. However, some block coordinates
|
||||
// may contain a redstone wire that does not directly connect to the
|
||||
// one being expanded. To avoid redundant calculations and confusing
|
||||
// cross-talk, those neighboring positions are not included.
|
||||
if (update_redstone[i] || upd2.type != UpdateNode.Type.REDSTONE) {
|
||||
neighbor_nodes[i] = upd2;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the directions from which the redstone signal may have come from. This
|
||||
// checks for redstone wire at the same Y level and also Y+1 and Y-1, relative to the
|
||||
// block being expanded.
|
||||
final boolean fromWest = (neighbor_nodes[0].visited || neighbor_nodes[7].visited || neighbor_nodes[8].visited);
|
||||
final boolean fromEast = (neighbor_nodes[1].visited || neighbor_nodes[12].visited || neighbor_nodes[13].visited);
|
||||
final boolean fromNorth = (neighbor_nodes[4].visited || neighbor_nodes[17].visited || neighbor_nodes[20].visited);
|
||||
final boolean fromSouth = (neighbor_nodes[5].visited || neighbor_nodes[18].visited || neighbor_nodes[21].visited);
|
||||
|
||||
int cx = 0, cz = 0;
|
||||
if (fromWest) cx += 1;
|
||||
if (fromEast) cx -= 1;
|
||||
if (fromNorth) cz += 1;
|
||||
if (fromSouth) cz -= 1;
|
||||
|
||||
int heading;
|
||||
if (cx==0 && cz==0) {
|
||||
// If there is no clear direction, try to inherit the heading from ancestor nodes.
|
||||
heading = computeHeading(upd1.xbias, upd1.zbias);
|
||||
|
||||
// Propagate that heading to descendent nodes.
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode nn = neighbor_nodes[i];
|
||||
if (nn != null) {
|
||||
nn.xbias = upd1.xbias;
|
||||
nn.zbias = upd1.zbias;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cx != 0 && cz != 0) {
|
||||
// If the heading is somewhat ambiguous, try to disambiguate based on
|
||||
// ancestor nodes.
|
||||
if (upd1.xbias != 0) cz = 0;
|
||||
if (upd1.zbias != 0) cx = 0;
|
||||
}
|
||||
heading = computeHeading(cx, cz);
|
||||
|
||||
// Propagate that heading to descendent nodes.
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode nn = neighbor_nodes[i];
|
||||
if (nn != null) {
|
||||
nn.xbias = cx;
|
||||
nn.zbias = cz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder neighboring UpdateNode objects according to the forward direction
|
||||
// determined above.
|
||||
orientNeighbors(neighbor_nodes, upd1.neighbor_nodes, heading);
|
||||
}
|
||||
|
||||
/*
|
||||
* For any redstone wire block in layer N, inform neighbors to recompute their states
|
||||
* in layers N+1 and N+2;
|
||||
*/
|
||||
private void propagateChanges(final World worldIn, final UpdateNode upd1, final int layer) {
|
||||
if (upd1.neighbor_nodes == null) {
|
||||
// If this node has not been expanded yet, find its neigbors
|
||||
findNeighbors(worldIn, upd1);
|
||||
}
|
||||
|
||||
final BlockPos pos = upd1.self;
|
||||
final int x = pos.getX();
|
||||
final int y = pos.getY();
|
||||
final int z = pos.getZ();
|
||||
|
||||
// Make sure there are enough layers in the list
|
||||
//while (updateLayers.size() <= layer+2) updateLayers.add(new ArrayList<UpdateNode>());
|
||||
|
||||
// All neighbors may be scheduled for layer N+1
|
||||
final int layer1 = layer + 1;
|
||||
|
||||
// If the node being updated (upd1) has already been expanded, then merely
|
||||
// schedule updates to its neighbors.
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode upd2 = upd1.neighbor_nodes[i];
|
||||
|
||||
// This test ensures that an UpdateNode is never scheduled to the same layer
|
||||
// more than once. Also, skip non-connecting redstone wire blocks
|
||||
if (upd2 != null && layer1 > upd2.layer) {
|
||||
upd2.layer = layer1;
|
||||
updateQueue1.add(upd2);
|
||||
|
||||
// Keep track of which block updated this neighbor
|
||||
upd2.parent = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes above and below are scheduled ALSO for layer N+2
|
||||
final int layer2 = layer + 2;
|
||||
|
||||
// Repeat of the loop above, but only for the first four (above and below) neighbors
|
||||
// and for layer N+2;
|
||||
for (int i=0; i<4; i++) {
|
||||
final UpdateNode upd2 = upd1.neighbor_nodes[i];
|
||||
if (upd2 != null && layer2 > upd2.layer) {
|
||||
upd2.layer = layer2;
|
||||
updateQueue2.add(upd2);
|
||||
upd2.parent = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The breadth-first search below will send block updates to blocks
|
||||
// that are not redstone wire. If one of those updates results in
|
||||
// a distant redstone wire getting an update, then this.neighborChanged
|
||||
// will get called. This would be a reentrant call, and
|
||||
// it is necessary to properly integrate those updates into the
|
||||
// on-going search through redstone wire. Thus, we make the layer
|
||||
// currently being processed visible at the object level.
|
||||
|
||||
// The current layer being processed by the breadth-first search
|
||||
private int currentWalkLayer = 0;
|
||||
|
||||
private void shiftQueue() {
|
||||
final List<UpdateNode> t = updateQueue0;
|
||||
t.clear();
|
||||
updateQueue0 = updateQueue1;
|
||||
updateQueue1 = updateQueue2;
|
||||
updateQueue2 = t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a breadth-first (layer by layer) traversal through redstone
|
||||
* wire blocks, propagating value changes to neighbors in an order
|
||||
* that is a function of distance from the initial call to
|
||||
* this.neighborChanged.
|
||||
*/
|
||||
private void breadthFirstWalk(final World worldIn) {
|
||||
shiftQueue();
|
||||
currentWalkLayer = 1;
|
||||
|
||||
// Loop over all layers
|
||||
while (updateQueue0.size()>0 || updateQueue1.size()>0) {
|
||||
// Get the set of blocks in this layer
|
||||
final List<UpdateNode> thisLayer = updateQueue0;
|
||||
//if (thisLayer.size() < 1) {
|
||||
//shiftQueue();
|
||||
//currentWalkLayer++;
|
||||
//continue;
|
||||
//}
|
||||
|
||||
// Loop over all blocks in the layer. Recall that
|
||||
// this is a List, preserving the insertion order of
|
||||
// left-to-right based on direction of information flow.
|
||||
for (UpdateNode upd : thisLayer) {
|
||||
if (upd.type == UpdateNode.Type.REDSTONE) {
|
||||
// If the node is is redstone wire,
|
||||
// schedule updates to neighbors if its value
|
||||
// has changed.
|
||||
updateNode(worldIn, upd, currentWalkLayer);
|
||||
} else {
|
||||
// If this block is not redstone wire, send a block update.
|
||||
// Redstone wire blocks get state updates, but they don't
|
||||
// need block updates. Only non-redstone neighbors need updates.
|
||||
|
||||
// The following function is called "neighborChanged" in the latest
|
||||
// deobfuscated names. World.func_190524_a is called from
|
||||
// World.notifyNeighborsOfStateChange, and
|
||||
// notifyNeighborsOfStateExcept. We don't use
|
||||
// World.notifyNeighborsOfStateChange here, since we are
|
||||
// already keeping track of all of the neighbor positions
|
||||
// that need to be updated. All on its own, handling neighbors
|
||||
// this way reduces block updates by 1/3 (24 instead of 36).
|
||||
worldIn.neighborChanged(upd.self, wire, upd.parent);
|
||||
}
|
||||
}
|
||||
|
||||
// Move on to the next layer
|
||||
shiftQueue();
|
||||
currentWalkLayer++;
|
||||
}
|
||||
|
||||
currentWalkLayer = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Normally, when Minecraft is computing redstone wire power changes, and a wire power level
|
||||
* change sends a block update to a neighboring functional component (e.g. piston, repeater, etc.),
|
||||
* those updates are queued. Only once all redstone wire updates are complete will any component
|
||||
* action generate any further block updates to redstone wire. Instant repeater lines, for instance,
|
||||
* will process all wire updates for one redstone line, after which the pistons will zero-tick,
|
||||
* after which the next redstone line performs all of its updates. Thus, each wire is processed in its
|
||||
* own discrete wave.
|
||||
*
|
||||
* However, there are some corner cases where this pattern breaks, with a proof of concept discovered
|
||||
* by Rays Works, which works the same in vanilla. The scenario is as follows:
|
||||
* (1) A redstone wire is conducting a signal.
|
||||
* (2) Partway through that wave of updates, a neighbor is updated that causes an update to a completely
|
||||
* separate redstone wire.
|
||||
* (3) This results in a call to RedstoneWireBlock.neighborChanged for that other wire, in the middle of
|
||||
* an already on-going propagation through the first wire.
|
||||
*
|
||||
* The vanilla code, being depth-first, would end up fully processing the second wire before going back
|
||||
* to finish processing the first one. (Although technically, vanilla has no special concept of "being
|
||||
* in the middle" of processing updates to a wire.) For the breadth-first algorithm, we give this
|
||||
* situation special handling, where the updates for the second wire are incorporated into the schedule
|
||||
* for the first wire, and then the callstack is allowed to unwind back to the on-going search loop in
|
||||
* order to continue processing both the first and second wire in the order of distance from the initial
|
||||
* trigger.
|
||||
*/
|
||||
private BlockState scheduleReentrantNeighborChanged(final World worldIn, final BlockPos pos, final BlockState newState, final BlockPos source)
|
||||
{
|
||||
if (source != null) {
|
||||
// If the cause of the redstone wire update is known, we can use that to help determine
|
||||
// direction of information flow.
|
||||
UpdateNode src = nodeCache.get(source);
|
||||
if (src == null) {
|
||||
src = new UpdateNode();
|
||||
src.self = source;
|
||||
src.parent = source;
|
||||
src.visited = true;
|
||||
identifyNode(worldIn, src);
|
||||
nodeCache.put(source, src);
|
||||
}
|
||||
}
|
||||
|
||||
// Find or generate a node for the redstone block position receiving the update
|
||||
UpdateNode upd = nodeCache.get(pos);
|
||||
if (upd == null) {
|
||||
upd = new UpdateNode();
|
||||
upd.self = pos;
|
||||
upd.parent = pos;
|
||||
upd.visited = true;
|
||||
identifyNode(worldIn, upd);
|
||||
nodeCache.put(pos, upd);
|
||||
}
|
||||
upd.currentState = newState;
|
||||
|
||||
// Receiving this block update may mean something in the world changed.
|
||||
// Therefore we clear the cached block info about all neighbors of
|
||||
// the position receiving the update and then re-identify what they are.
|
||||
if (upd.neighbor_nodes != null) {
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode upd2 = upd.neighbor_nodes[i];
|
||||
if (upd2 == null) continue;
|
||||
upd2.type = UpdateNode.Type.UNKNOWN;
|
||||
upd2.currentState = null;
|
||||
identifyNode(worldIn, upd2);
|
||||
}
|
||||
}
|
||||
|
||||
// The block at 'pos' is a redstone wire and has been updated already by calling
|
||||
// wire.calculateCurrentChanges, so we don't schedule that. However, we do need
|
||||
// to schedule its neighbors. By passing the current value of 'currentWalkLayer' to
|
||||
// propagateChanges, the neighbors of 'pos' are schedule for layers currentWalkLayer+1
|
||||
// and currentWalkLayer+2.
|
||||
propagateChanges(worldIn, upd, currentWalkLayer);
|
||||
|
||||
// Return here. The call stack will unwind back to the first call to
|
||||
// updateSurroundingRedstone, whereupon the new updates just scheduled will
|
||||
// be propagated. This also facilitates elimination of superfluous and
|
||||
// redundant block updates.
|
||||
return newState;
|
||||
}
|
||||
|
||||
/*
|
||||
* New version of pre-existing updateSurroundingRedstone, which is called from
|
||||
* wire.updateSurroundingRedstone, which is called from wire.neighborChanged and a
|
||||
* few other methods in RedstoneWireBlock. This sets off the breadth-first
|
||||
* walk through all redstone dust connected to the initial position triggered.
|
||||
*/
|
||||
public BlockState updateSurroundingRedstone(final World worldIn, final BlockPos pos, final BlockState state, final BlockPos source)
|
||||
{
|
||||
// Check this block's neighbors and see if its power level needs to change
|
||||
// Use the calculateCurrentChanges method in RedstoneWireBlock since we have no
|
||||
// cached block states at this point.
|
||||
final BlockState newState = wire.func_212568_b(worldIn, pos, state);
|
||||
|
||||
// If no change, exit
|
||||
if (newState == state) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Check to see if this update was received during an on-going breadth first search
|
||||
if (currentWalkLayer>0 || nodeCache.size()>0) {
|
||||
// As breadthFirstWalk progresses, it sends block updates to neighbors. Some of those
|
||||
// neighbors may affect the world so as to cause yet another redstone wire block to receive
|
||||
// an update. If that happens, we need to integrate those redstone wire updates into the
|
||||
// already on-going graph walk being performed by breadthFirstWalk.
|
||||
return scheduleReentrantNeighborChanged(worldIn, pos, newState, source);
|
||||
}
|
||||
// If there are no on-going walks through redstone wire, then start a new walk.
|
||||
|
||||
// If the source of the block update to the redstone wire at 'pos' is known, we can use
|
||||
// that to help determine the direction of information flow.
|
||||
if (source != null) {
|
||||
final UpdateNode src = new UpdateNode();
|
||||
src.self = source;
|
||||
src.parent = source;
|
||||
src.visited = true;
|
||||
nodeCache.put(source, src);
|
||||
identifyNode(worldIn, src);
|
||||
}
|
||||
|
||||
// Create a node representing the block at 'pos', and then propagate updates
|
||||
// to its neighbors. As stated above, the call to wire.calculateCurrentChanges
|
||||
// already performs the update to the block at 'pos', so it is not added to the schedule.
|
||||
final UpdateNode upd = new UpdateNode();
|
||||
upd.self = pos;
|
||||
upd.parent = source!=null ? source : pos;
|
||||
upd.currentState = newState;
|
||||
upd.type = UpdateNode.Type.REDSTONE;
|
||||
upd.visited = true;
|
||||
nodeCache.put(pos, upd);
|
||||
propagateChanges(worldIn, upd, 0);
|
||||
|
||||
// Perform the walk over all directly reachable redstone wire blocks, propagating wire value
|
||||
// updates in a breadth first order out from the initial update received for the block at 'pos'.
|
||||
breadthFirstWalk(worldIn);
|
||||
|
||||
// With the whole search completed, clear the list of all known blocks.
|
||||
// We do not want to keep around state information that may be changed by other code.
|
||||
// In theory, we could cache the neighbor block positions, but that is a separate
|
||||
// optimization.
|
||||
nodeCache.clear();
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
|
||||
// For any array of neighbors in an UpdateNode object, these are always
|
||||
// the indices of the four immediate neighbors at the same Y coordinate.
|
||||
private static final int[] rs_neighbors = {4, 5, 6, 7};
|
||||
private static final int[] rs_neighbors_up = {9, 11, 13, 15};
|
||||
private static final int[] rs_neighbors_dn = {8, 10, 12, 14};
|
||||
|
||||
/*
|
||||
* Updated calculateCurrentChanges that is optimized for speed and uses
|
||||
* the UpdateNode's neighbor array to find the redstone states of neighbors
|
||||
* that might power it.
|
||||
*/
|
||||
private BlockState calculateCurrentChanges(final World worldIn, final UpdateNode upd)
|
||||
{
|
||||
BlockState state = upd.currentState;
|
||||
final int i = state.get(RedstoneWireBlock.POWER);
|
||||
int j = 0;
|
||||
j = this.getMaxCurrentStrength(upd, j);
|
||||
int l = 0;
|
||||
|
||||
wire.setCanProvidePower(false);
|
||||
// Unfortunately, World.isBlockIndirectlyGettingPowered is complicated,
|
||||
// and I'm not ready to try to replicate even more functionality from
|
||||
// elsewhere in Minecraft into this accelerator. So sadly, we must
|
||||
// suffer the performance hit of this very expensive call. If there
|
||||
// is consistency to what this call returns, we may be able to cache it.
|
||||
final int k = worldIn.getRedstonePowerFromNeighbors(upd.self);
|
||||
wire.setCanProvidePower(true);
|
||||
|
||||
// The variable 'k' holds the maximum redstone power value of any adjacent blocks.
|
||||
// If 'k' has the highest level of all neighbors, then the power level of this
|
||||
// redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the
|
||||
// following loop can affect the power level of the wire. Therefore, the loop is
|
||||
// skipped if k is already 15.
|
||||
if (k<15) {
|
||||
if (upd.neighbor_nodes == null) {
|
||||
// If this node's neighbors are not known, expand the node
|
||||
findNeighbors(worldIn, upd);
|
||||
}
|
||||
|
||||
// These remain constant, so pull them out of the loop.
|
||||
// Regardless of which direction is forward, the UpdateNode for the
|
||||
// position directly above the node being calculated is always
|
||||
// at index 1.
|
||||
UpdateNode center_up = upd.neighbor_nodes[1];
|
||||
boolean center_up_is_cube = center_up.currentState.isNormalCube();
|
||||
|
||||
for (int m=0; m<4; m++) {
|
||||
// Get the neighbor array index of each of the four cardinal
|
||||
// neighbors.
|
||||
int n = rs_neighbors[m];
|
||||
|
||||
// Get the max redstone power level of each of the cardinal
|
||||
// neighbors
|
||||
UpdateNode neighbor = upd.neighbor_nodes[n];
|
||||
l = this.getMaxCurrentStrength(neighbor, l);
|
||||
|
||||
// Also check the positions above and below the cardinal
|
||||
// neighbors
|
||||
boolean neighbor_is_cube = neighbor.currentState.isNormalCube();
|
||||
if (!neighbor_is_cube) {
|
||||
UpdateNode neighbor_down = upd.neighbor_nodes[rs_neighbors_dn[m]];
|
||||
l = this.getMaxCurrentStrength(neighbor_down, l);
|
||||
} else
|
||||
if (!center_up_is_cube) {
|
||||
UpdateNode neighbor_up = upd.neighbor_nodes[rs_neighbors_up[m]];
|
||||
l = this.getMaxCurrentStrength(neighbor_up, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The new code sets this redstonewire block's power level to the highest neighbor
|
||||
// minus 1. This usually results in wire power levels dropping by 2 at a time.
|
||||
// This optimization alone has no impact on opdate order, only the number of updates.
|
||||
j = l-1;
|
||||
|
||||
// If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will
|
||||
// always be in the range of 0 to 15, the following if will correct that.
|
||||
if (k>j) j=k;
|
||||
|
||||
if (i != j) {
|
||||
// If the power level has changed from its previous value, compute a new state
|
||||
// and set it in the world.
|
||||
// Possible optimization: Don't commit state changes to the world until they
|
||||
// need to be known by some nearby non-redstone-wire block.
|
||||
state = state.with(RedstoneWireBlock.POWER, j);
|
||||
worldIn.setBlockState(upd.self, state, 2);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimized function to compute a redstone wire's power level based on cached
|
||||
* state.
|
||||
*/
|
||||
private static int getMaxCurrentStrength(final UpdateNode upd, final int strength) {
|
||||
if (upd.type != UpdateNode.Type.REDSTONE) return strength;
|
||||
final int i = upd.currentState.get(RedstoneWireBlock.POWER);
|
||||
return i > strength ? i : strength;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.fakes.ExperienceOrbInterface;
|
||||
import carpet.mixins.ExperienceOrbEntityMixin;
|
||||
import net.minecraft.client.network.packet.PlaySoundFromEntityS2CPacket;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.ExperienceOrbEntity;
|
||||
import net.minecraft.entity.ai.TargetPredicate;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class XPcombine
|
||||
{
|
||||
public static void searchForOtherXPNearbyCarpet(ExperienceOrbEntity first)
|
||||
{
|
||||
for (ExperienceOrbEntity entityxp : first.world.getEntities(ExperienceOrbEntity.class, first.getBoundingBox().expand(0.5D, 0.0D, 0.5D)))
|
||||
{
|
||||
combineItems(first, entityxp);
|
||||
}
|
||||
}
|
||||
private static long tone = 0;
|
||||
private static boolean combineItems(ExperienceOrbEntity first, ExperienceOrbEntity other)
|
||||
{
|
||||
if (
|
||||
first == other || first.world.isClient
|
||||
|| !first.isAlive() || !other.isAlive()
|
||||
|| first.pickupDelay == 32767 || other.pickupDelay == 32767
|
||||
|| first.age == -32768 || other.age == -32768
|
||||
|| ((ExperienceOrbInterface)first).getCombineDelay() != 0 || ((ExperienceOrbInterface)other).getCombineDelay() != 0
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = getTextureByXP(first.getExperienceAmount());
|
||||
((ExperienceOrbInterface) first).setAmount(first.getExperienceAmount() + other.getExperienceAmount());
|
||||
((ExperienceOrbInterface) first).setCombineDelay(Math.max(first.pickupDelay, other.pickupDelay));
|
||||
first.age = Math.min(first.age, other.age);
|
||||
other.remove();
|
||||
|
||||
|
||||
ExperienceOrbEntity newOrb;
|
||||
if (getTextureByXP(first.getExperienceAmount()) != size)
|
||||
{
|
||||
newOrb = new ExperienceOrbEntity(EntityType.EXPERIENCE_ORB, first.world);
|
||||
newOrb.setPositionAndAngles(first.x, first.y, first.z, first.yaw, first.pitch);
|
||||
((ExperienceOrbInterface)newOrb).setAmount(first.getExperienceAmount());
|
||||
|
||||
first.world.spawnEntity(newOrb);
|
||||
first.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
((ExperienceOrbInterface) first).setCombineDelay(50);
|
||||
newOrb = first;
|
||||
}
|
||||
newOrb.setVelocity(first.getVelocity().add(new Vec3d(
|
||||
(first.world.random.nextDouble() * 1.0D - 0.5D),
|
||||
(first.world.random.nextDouble() * 0.5D),
|
||||
(first.world.random.nextDouble() * 1.0D - 0.5D)
|
||||
)));
|
||||
double pitch = 0.5+Math.log(1+(tone%12))/Math.log(12);
|
||||
|
||||
tone++;
|
||||
|
||||
CarpetSettings.LOG.error("Playing tone: "+pitch);
|
||||
|
||||
newOrb.world.playSoundFromEntity(
|
||||
null,
|
||||
newOrb,
|
||||
SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP,
|
||||
SoundCategory.BLOCKS, (float)pitch , (float)pitch); //newOrb.world.random.nextFloat()*0.5F+0.5F
|
||||
|
||||
/*newOrb.world.playSound(newOrb.x, newOrb.y, newOrb.z,
|
||||
SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP,
|
||||
SoundCategory.BLOCKS,
|
||||
(float)pitch,
|
||||
newOrb.world.random.nextFloat()*0.5F+0.5F,
|
||||
true);*/
|
||||
return true;
|
||||
}
|
||||
|
||||
// COPY FROM CLIENT CODE
|
||||
private static int getTextureByXP(int xpValue)
|
||||
{
|
||||
if (xpValue >= 2477)
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
else if (xpValue >= 1237)
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
else if (xpValue >= 617)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
else if (xpValue >= 307)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
else if (xpValue >= 149)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
else if (xpValue >= 73)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
else if (xpValue >= 37)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if (xpValue >= 17)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else if (xpValue >= 7)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return xpValue >= 3 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package carpet.mixins.carpetServer;
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
@@ -0,0 +1,47 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.fakes.ExperienceOrbInterface;
|
||||
import carpet.helpers.XPcombine;
|
||||
import net.minecraft.entity.ExperienceOrbEntity;
|
||||
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(ExperienceOrbEntity.class)
|
||||
public abstract class ExperienceOrbEntityMixin implements ExperienceOrbInterface
|
||||
{
|
||||
@Shadow private int amount;
|
||||
|
||||
public void setAmount(int amount) { this.amount = amount; }
|
||||
|
||||
public int combineDelay = 50;
|
||||
|
||||
public int getCombineDelay() { return combineDelay; }
|
||||
public void setCombineDelay(int what) { combineDelay = what; }
|
||||
|
||||
@Inject(method = "tick",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/entity/ExperienceOrbEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V",
|
||||
shift = At.Shift.AFTER
|
||||
))
|
||||
void checkCombineAtTick(CallbackInfo ci)
|
||||
{
|
||||
if (CarpetSettings.getBool("combineXPOrbs"))
|
||||
{
|
||||
if (getCombineDelay() > 0)
|
||||
{
|
||||
setCombineDelay(getCombineDelay()-1);
|
||||
}
|
||||
|
||||
if (getCombineDelay() == 0)
|
||||
{
|
||||
XPcombine.searchForOtherXPNearbyCarpet((ExperienceOrbEntity) (Object) this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package carpet.mixins.carpetServer;
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package carpet.mixins.carpetServer;
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package carpet.mixins.carpetServer;
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package carpet.mixins.carpetServer;
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import com.mojang.authlib.GameProfileRepository;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package carpet.mixins.logging;
|
||||
package carpet.mixins;
|
||||
|
||||
import net.minecraft.client.network.packet.PlayerListHeaderS2CPacket;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -0,0 +1,372 @@
|
||||
package carpet.utils;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CarpetProfiler
|
||||
{
|
||||
private static final HashMap<String, Long> time_repo = new HashMap<>();
|
||||
public static int tick_health_requested = 0;
|
||||
private static int tick_health_elapsed = 0;
|
||||
private static int test_type = 0; //1 for ticks, 2 for entities;
|
||||
private static String current_section = null;
|
||||
private static long current_section_start = 0;
|
||||
private static long current_tick_start = 0;
|
||||
private static String [] GENERAL_SECTIONS = {"Network", "Autosave"};
|
||||
private static String [] DIMENSIONS = {"Overworld","The End","The Nether"};
|
||||
private static String [] SECTIONS = {"Spawning","Blocks","Entities","Tile Entities","Entities(client)","Tile Entities(client)","Villages"};
|
||||
|
||||
public static void prepare_tick_report(int ticks)
|
||||
{
|
||||
//maybe add so it only spams the sending player, but honestly - all may want to see it
|
||||
time_repo.clear();
|
||||
test_type = 1;
|
||||
time_repo.put("tick",0L);
|
||||
time_repo.put("Network",0L);
|
||||
time_repo.put("Autosave",0L);
|
||||
for (String section: GENERAL_SECTIONS)
|
||||
{
|
||||
time_repo.put(section,0L);
|
||||
}
|
||||
for (String level: DIMENSIONS)
|
||||
{
|
||||
for (String section: SECTIONS)
|
||||
{
|
||||
time_repo.put(level+"."+section,0L);
|
||||
}
|
||||
}
|
||||
|
||||
tick_health_elapsed = ticks;
|
||||
tick_health_requested = ticks;
|
||||
current_tick_start = 0L;
|
||||
current_section_start = 0L;
|
||||
current_section = null;
|
||||
|
||||
}
|
||||
|
||||
public static void start_section(String dimension, String name)
|
||||
{
|
||||
if (tick_health_requested == 0L || test_type != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current_section != null)
|
||||
{
|
||||
end_current_section();
|
||||
}
|
||||
String key = name;
|
||||
if (dimension != null)
|
||||
{
|
||||
key = dimension+"."+name;
|
||||
}
|
||||
current_section = key;
|
||||
current_section_start = System.nanoTime();
|
||||
}
|
||||
|
||||
|
||||
public static class ProfilerToken
|
||||
{
|
||||
public String section;
|
||||
public long start;
|
||||
public ProfilerToken(String name, long start)
|
||||
{
|
||||
this.section = name;
|
||||
this.start = start;
|
||||
}
|
||||
}
|
||||
|
||||
public static ProfilerToken start_section_concurrent(String dimension, String name, boolean isRemote)
|
||||
{
|
||||
if (tick_health_requested == 0L || test_type != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String key = name;
|
||||
if (dimension != null)
|
||||
{
|
||||
key = dimension+"."+name;
|
||||
}
|
||||
if (isRemote) key += "(client)";
|
||||
long time = System.nanoTime();
|
||||
return new ProfilerToken(key, time);
|
||||
}
|
||||
|
||||
public static ProfilerToken start_entity_section(String dimension, Entity e)
|
||||
{
|
||||
if (tick_health_requested == 0L || test_type != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String section = dimension+"."+ Registry.ENTITY_TYPE.getId(e.getType()).toString().replaceFirst("minecraft:","");
|
||||
if (e.getEntityWorld().isClient)
|
||||
section += "(client)";
|
||||
long section_start = System.nanoTime();
|
||||
return new ProfilerToken(section, section_start);
|
||||
}
|
||||
|
||||
public static ProfilerToken start_tileentity_section(String dimension, BlockEntity e)
|
||||
{
|
||||
if (tick_health_requested == 0L || test_type != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String section = dimension+"."+ BlockEntityType.getId(e.getType()).toString().replaceFirst("minecraft:","");
|
||||
if (e.getWorld() == null)
|
||||
section += "??";
|
||||
else if (e.getWorld().isClient)
|
||||
section += "(client)";
|
||||
long section_start = System.nanoTime();
|
||||
return new ProfilerToken(section, section_start);
|
||||
}
|
||||
|
||||
public static void end_current_section()
|
||||
{
|
||||
if (tick_health_requested == 0L || test_type != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
long end_time = System.nanoTime();
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current_section == null)
|
||||
{
|
||||
CarpetSettings.LOG.error("finishing section that hasn't started");
|
||||
return;
|
||||
}
|
||||
//CarpetSettings.LOG.error("finishing section "+current_section);
|
||||
time_repo.put(current_section,time_repo.get(current_section)+end_time-current_section_start);
|
||||
current_section = null;
|
||||
current_section_start = 0;
|
||||
}
|
||||
|
||||
public static void end_current_section_concurrent(ProfilerToken tok)
|
||||
{
|
||||
if (tick_health_requested == 0L || test_type != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
long end_time = System.nanoTime();
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (tok == null)
|
||||
{
|
||||
CarpetSettings.LOG.error("finishing section that hasn't started");
|
||||
return;
|
||||
}
|
||||
//CarpetSettings.LOG.error("finishing section "+current_section);
|
||||
time_repo.put(tok.section,time_repo.get(tok.section)+end_time-tok.start);
|
||||
}
|
||||
|
||||
public static void end_current_entity_section(ProfilerToken tok)
|
||||
{
|
||||
if (tick_health_requested == 0L || test_type != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
long end_time = System.nanoTime();
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (tok == null)
|
||||
{
|
||||
CarpetSettings.LOG.error("finishing entity/TE section that hasn't started");
|
||||
return;
|
||||
}
|
||||
String time_section = "t."+tok.section;
|
||||
String count_section = "c."+tok.section;
|
||||
time_repo.put(time_section,time_repo.getOrDefault(time_section,0L)+end_time-tok.start);
|
||||
time_repo.put(count_section,time_repo.getOrDefault(count_section,0L)+1);
|
||||
}
|
||||
|
||||
public static void start_tick_profiling()
|
||||
{
|
||||
current_tick_start = System.nanoTime();
|
||||
}
|
||||
|
||||
public static void end_tick_profiling(MinecraftServer server)
|
||||
{
|
||||
if (current_tick_start == 0L)
|
||||
{
|
||||
return;
|
||||
}
|
||||
time_repo.put("tick",time_repo.get("tick")+System.nanoTime()-current_tick_start);
|
||||
tick_health_elapsed --;
|
||||
//CarpetSettings.LOG.error("tick count current at "+time_repo.get("tick"));
|
||||
if (tick_health_elapsed <= 0)
|
||||
{
|
||||
finalize_tick_report(server);
|
||||
}
|
||||
}
|
||||
|
||||
public static void finalize_tick_report(MinecraftServer server)
|
||||
{
|
||||
if (test_type == 1)
|
||||
{
|
||||
finalize_tick_report_for_time(server);
|
||||
}
|
||||
if (test_type == 2)
|
||||
{
|
||||
finalize_tick_report_for_entities(server);
|
||||
}
|
||||
cleanup_tick_report();
|
||||
}
|
||||
|
||||
public static void cleanup_tick_report()
|
||||
{
|
||||
time_repo.clear();
|
||||
time_repo.put("tick",0L);
|
||||
test_type = 0;
|
||||
tick_health_elapsed = 0;
|
||||
tick_health_requested = 0;
|
||||
current_tick_start = 0L;
|
||||
current_section_start = 0L;
|
||||
current_section = null;
|
||||
|
||||
}
|
||||
|
||||
public static void finalize_tick_report_for_time(MinecraftServer server)
|
||||
{
|
||||
//print stats
|
||||
long total_tick_time = time_repo.get("tick");
|
||||
double divider = 1.0D/tick_health_requested/1000000;
|
||||
Messenger.print_server_message(server, String.format("Average tick time: %.3fms",divider*total_tick_time));
|
||||
long accumulated = 0L;
|
||||
|
||||
for (String section: GENERAL_SECTIONS)
|
||||
{
|
||||
double amount = divider*time_repo.get(section);
|
||||
if (amount > 0.01)
|
||||
{
|
||||
accumulated += time_repo.get(section);
|
||||
Messenger.print_server_message(server, String.format("%s: %.3fms", section, amount));
|
||||
}
|
||||
}
|
||||
|
||||
for (String dimension: DIMENSIONS)
|
||||
{
|
||||
boolean hasSomethin = false;
|
||||
for (String section: SECTIONS)
|
||||
{
|
||||
double amount = divider*time_repo.get(dimension+"."+section);
|
||||
if (amount > 0.01)
|
||||
{
|
||||
hasSomethin = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(hasSomethin))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Messenger.print_server_message(server, dimension+":");
|
||||
for (String section: SECTIONS)
|
||||
{
|
||||
double amount = divider*time_repo.get(dimension+"."+section);
|
||||
if (amount > 0.01)
|
||||
{
|
||||
if (!(section.endsWith("(client)")))
|
||||
accumulated += time_repo.get(dimension+"."+section);
|
||||
Messenger.print_server_message(server, String.format(" - %s: %.3fms", section, amount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long rest = total_tick_time-accumulated;
|
||||
|
||||
Messenger.print_server_message(server, String.format("The Rest, whatever that might be: %.3fms",divider*rest));
|
||||
}
|
||||
|
||||
public static void finalize_tick_report_for_entities(MinecraftServer server)
|
||||
{
|
||||
//print stats
|
||||
long total_tick_time = time_repo.get("tick");
|
||||
double divider = 1.0D/tick_health_requested/1000000;
|
||||
double divider_1 = 1.0D/(tick_health_requested-1)/1000000;
|
||||
Messenger.print_server_message(server, String.format("Average tick time: %.3fms",divider*total_tick_time));
|
||||
time_repo.remove("tick");
|
||||
Messenger.print_server_message(server, "Top 10 counts:");
|
||||
int total = 0;
|
||||
for ( Map.Entry<String, Long> entry : time_repo.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()) )
|
||||
{
|
||||
if (entry.getKey().startsWith("t."))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
total++;
|
||||
if (total > 10)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String[] parts = entry.getKey().split("\\.");
|
||||
String dim = parts[1];
|
||||
String name = parts[2];
|
||||
int penalty = name.endsWith("(client)") ? 1 :0;
|
||||
Messenger.print_server_message(server, String.format(" - %s in %s: %.3f",name, dim, 1.0D*entry.getValue()/(tick_health_requested-penalty)));
|
||||
}
|
||||
Messenger.print_server_message(server, "Top 10 grossing:");
|
||||
total = 0;
|
||||
for ( Map.Entry<String, Long> entry : time_repo.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()) )
|
||||
{
|
||||
if (entry.getKey().startsWith("c."))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
total++;
|
||||
if (total > 10)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String[] parts = entry.getKey().split("\\.");
|
||||
String dim = parts[1];
|
||||
String name = parts[2];
|
||||
double applicableDivider = name.endsWith("(client)") ? divider :divider_1;
|
||||
Messenger.print_server_message(server, String.format(" - %s in %s: %.3fms",name, dim, applicableDivider*entry.getValue()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void prepare_entity_report(int ticks)
|
||||
{
|
||||
//maybe add so it only spams the sending player, but honestly - all may want to see it
|
||||
time_repo.clear();
|
||||
time_repo.put("tick",0L);
|
||||
test_type = 2;
|
||||
tick_health_elapsed = ticks;
|
||||
tick_health_requested = ticks;
|
||||
current_tick_start = 0L;
|
||||
current_section_start = 0L;
|
||||
current_section = null;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package carpet.utils;
|
||||
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.chat.BaseComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
// not really needed anymore
|
||||
public class EntityInfo
|
||||
{
|
||||
private static String makeTime(long ticks)
|
||||
{
|
||||
long secs = ticks/20;
|
||||
if (secs < 60)
|
||||
{
|
||||
return String.format("%d\"", secs);
|
||||
}
|
||||
if (secs < 60*60)
|
||||
{
|
||||
return String.format("%d'%d\"", secs/60, secs%60);
|
||||
}
|
||||
|
||||
return String.format("%dh%d'%d\"", secs/60/60, (secs % (60*60))/60,(secs % (60*60))%60 );
|
||||
}
|
||||
|
||||
private static String display_item(ItemStack item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (item.isEmpty()) // func_190926_b()
|
||||
{
|
||||
return null;
|
||||
} // func_190916_E()
|
||||
String stackname = item.getAmount()>1?String.format("%dx%s",item.getAmount(), item.getDisplayName().getString()):item.getDisplayName().getString();
|
||||
if (item.isDamaged())
|
||||
{
|
||||
stackname += String.format(" %d/%d", item.getMaxUseTime()-item.getDamage(), item.getMaxUseTime());
|
||||
}
|
||||
if (item.hasEnchantments())
|
||||
{
|
||||
stackname += " ( ";
|
||||
Map<Enchantment, Integer> enchants = EnchantmentHelper.getEnchantments(item);
|
||||
for (Enchantment e: enchants.keySet())
|
||||
{
|
||||
int level = enchants.get(e);
|
||||
String enstring = e.getTextComponent(level).getString();
|
||||
stackname += enstring+" ";
|
||||
}
|
||||
stackname += ")";
|
||||
}
|
||||
return stackname;
|
||||
}
|
||||
|
||||
public static String entity_short_string(Entity e)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
return "None";
|
||||
}
|
||||
return String.format("%s at [%.1f, %.1f, %.1f]",e.getDisplayName().getString(), e.x, e.y, e.y);
|
||||
}
|
||||
|
||||
private static double get_speed(double internal)
|
||||
{
|
||||
return 43.1*internal;
|
||||
}
|
||||
|
||||
private static double get_horse_speed_percent(double internal)
|
||||
{
|
||||
double min = 0.45*0.25;
|
||||
double max = (0.45+0.9)*0.25;
|
||||
return 100*(internal-min)/(max-min);
|
||||
}
|
||||
|
||||
private static double get_horse_jump(double x)
|
||||
{
|
||||
return -0.1817584952 * x*x*x + 3.689713992 * x*x + 2.128599134 * x - 0.343930367;
|
||||
}
|
||||
|
||||
private static double get_horse_jump_percent(double internal)
|
||||
{
|
||||
double min = 0.4;
|
||||
double max = 1.0;
|
||||
return 100*(internal-min)/(max-min);
|
||||
}
|
||||
|
||||
public static List<BaseComponent> entityInfo(Entity e, World source_world)
|
||||
{
|
||||
List<BaseComponent> lst = new ArrayList<>();
|
||||
lst.add(Messenger.s("w Use /data get entity command"));
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import carpet.helpers.HopperCounter;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.logging.LoggerRegistry;
|
||||
import carpet.logging.logHelpers.PacketCounter;
|
||||
import carpet.mixins.logging.PlayerListHeaderS2CPacketMixin;
|
||||
import carpet.mixins.PlayerListHeaderS2CPacketMixin;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package carpet.utils;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.Material;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.entity.SpawnRestriction;
|
||||
import net.minecraft.entity.SpawnType;
|
||||
import net.minecraft.entity.WaterCreatureEntity;
|
||||
import net.minecraft.entity.mob.AmbientEntity;
|
||||
import net.minecraft.entity.mob.MobEntity;
|
||||
import net.minecraft.entity.mob.Monster;
|
||||
import net.minecraft.entity.passive.PassiveEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.SpawnHelper;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PerimeterDiagnostics
|
||||
{
|
||||
public static class Result
|
||||
{
|
||||
public int liquid;
|
||||
public int ground;
|
||||
public int specific;
|
||||
public List<BlockPos> samples;
|
||||
Result()
|
||||
{
|
||||
samples = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
private Biome.SpawnEntry sle;
|
||||
private ServerWorld worldServer;
|
||||
private EntityCategory ctype;
|
||||
private MobEntity el;
|
||||
private PerimeterDiagnostics(ServerWorld server, EntityCategory ctype, MobEntity el)
|
||||
{
|
||||
this.sle = null;
|
||||
this.worldServer = server;
|
||||
this.ctype = ctype;
|
||||
this.el = el;
|
||||
}
|
||||
|
||||
public static Result countSpots(ServerWorld worldserver, BlockPos epos, MobEntity el)
|
||||
{
|
||||
BlockPos pos;
|
||||
//List<BlockPos> samples = new ArrayList<BlockPos>();
|
||||
//if (el != null) CarpetSettings.LOG.error(String.format("Got %s to check",el.toString()));
|
||||
int eY = epos.getY();
|
||||
int eX = epos.getX();
|
||||
int eZ = epos.getZ();
|
||||
Result result = new Result();
|
||||
|
||||
//int ground_spawns = 0;
|
||||
//int liquid_spawns = 0;
|
||||
//int specific_spawns = 0;
|
||||
boolean add_water = false;
|
||||
boolean add_ground = false;
|
||||
EntityCategory ctype = null;
|
||||
|
||||
if (el != null)
|
||||
{
|
||||
if (el instanceof WaterCreatureEntity)
|
||||
{
|
||||
add_water = true;
|
||||
ctype = EntityCategory.WATER_CREATURE;
|
||||
}
|
||||
else if (el instanceof PassiveEntity)
|
||||
{
|
||||
add_ground = true;
|
||||
ctype = EntityCategory.CREATURE;
|
||||
}
|
||||
else if (el instanceof Monster)
|
||||
{
|
||||
add_ground = true;
|
||||
ctype = EntityCategory.MONSTER;
|
||||
}
|
||||
else if (el instanceof AmbientEntity)
|
||||
{
|
||||
ctype = EntityCategory.AMBIENT;
|
||||
}
|
||||
}
|
||||
PerimeterDiagnostics diagnostic = new PerimeterDiagnostics(worldserver,ctype,el);
|
||||
for (int x = -128; x <= 128; ++x)
|
||||
{
|
||||
for (int z = -128; z <= 128; ++z)
|
||||
{
|
||||
if (x*x + z*z > 128*128) // cut out a cyllinder first
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int y= 0; y < 256; ++y)
|
||||
{
|
||||
if ((Math.abs(y-eY)>128) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int distsq = (x)*(x)+(eY-y)*(eY-y)+(z)*(z);
|
||||
if (distsq > 128*128 || distsq < 24*24)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
pos = new BlockPos(eX+x, y, eZ+z);
|
||||
|
||||
BlockState iblockstate = worldserver.getBlockState(pos);
|
||||
BlockState iblockstate_down = worldserver.getBlockState(pos.down());
|
||||
BlockState iblockstate_up = worldserver.getBlockState(pos.up());
|
||||
|
||||
if ( iblockstate.getMaterial() == Material.WATER && iblockstate_down.getMaterial() == Material.WATER && !iblockstate_up.isSimpleFullBlock(worldserver, pos))
|
||||
{
|
||||
result.liquid++;
|
||||
if (add_water && diagnostic.check_entity_spawn(pos))
|
||||
{
|
||||
result.specific++;
|
||||
if (result.samples.size() < 10)
|
||||
{
|
||||
result.samples.add(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iblockstate_down.isSimpleFullBlock(worldserver, pos))
|
||||
{
|
||||
Block block = iblockstate_down.getBlock();
|
||||
boolean flag = block != Blocks.BEDROCK && block != Blocks.BARRIER;
|
||||
if( flag && SpawnHelper.isClearForSpawn(worldserver, pos, iblockstate, iblockstate.getFluidState()) && SpawnHelper.isClearForSpawn(worldserver, pos.up(), iblockstate_up, iblockstate_up.getFluidState()))
|
||||
{
|
||||
result.ground ++;
|
||||
if (add_ground && diagnostic.check_entity_spawn(pos))
|
||||
{
|
||||
result.specific++;
|
||||
if (result.samples.size() < 10)
|
||||
{
|
||||
result.samples.add(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//ashMap<String,Integer> result= new HashMap<>();
|
||||
//result.put("Potential in-water spawning spaces", liquid_spawns);
|
||||
//result.put("Potential on-ground spawning spaces", ground_spawns);
|
||||
//if (el != null) result.put(String.format("%s spawning spaces",el.getDisplayName().getUnformattedText()),specific_spawns);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private boolean check_entity_spawn(BlockPos pos)
|
||||
{
|
||||
if (sle == null || !worldServer.getChunkManager().getChunkGenerator().getEntitySpawnList(ctype, pos).contains(sle))
|
||||
{
|
||||
sle = null;
|
||||
for (Biome.SpawnEntry sle: worldServer.getChunkManager().getChunkGenerator().getEntitySpawnList(ctype, pos))
|
||||
{
|
||||
if (el.getType() == sle.type)
|
||||
{
|
||||
this.sle = sle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sle == null || !worldServer.getChunkManager().getChunkGenerator().getEntitySpawnList(ctype, pos).contains(sle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SpawnRestriction.Location spt = SpawnRestriction.getLocation(sle.type);
|
||||
|
||||
if (SpawnHelper.canSpawn(spt, worldServer, pos, sle.type))
|
||||
{
|
||||
el.setPositionAndAngles((float)pos.getX() + 0.5F, (float)pos.getY(), (float)pos.getZ()+0.5F, 0.0F, 0.0F);
|
||||
return el.canSpawn(worldServer) && el.canSpawn(worldServer, SpawnType.NATURAL) && worldServer.doesNotCollide(el); // check collision rules once they stop fiddling with them after 1.14.1
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,18 @@
|
||||
"package": "carpet.mixins",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"carpetServer/CommandManagerMixin",
|
||||
"carpetServer/MinecraftServerMixin",
|
||||
"logging/PlayerListHeaderS2CPacketMixin",
|
||||
"WeightedPickerEntryMixin"
|
||||
"CommandManagerMixin",
|
||||
"MinecraftServerMixin",
|
||||
"PlayerListHeaderS2CPacketMixin",
|
||||
"WeightedPickerEntryMixin",
|
||||
"ExperienceOrbEntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"carpetServer/MinecraftClientMixin",
|
||||
"carpetServer/IntegratedServerMixin"
|
||||
"MinecraftClientMixin",
|
||||
"IntegratedServerMixin"
|
||||
],
|
||||
"server": [
|
||||
"carpetServer/MinecraftDedicatedServerMixin"
|
||||
"MinecraftDedicatedServerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
],
|
||||
|
||||
"requires": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"fabric": "*"
|
||||
"fabricloader": ">=0.4.0"
|
||||
},
|
||||
"suggests": {
|
||||
"flamingo": "*"
|
||||
|
||||
Reference in new issue
Block a user