moved rest of the stuff. mixins needed still
This commit is contained in:
1 parent
394891ab5f
commit
5251bff2c7
10 files changed
+1075
-3
No files matched your search
@@ -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(), true));
|
||||
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, true));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int listEntitiesOfType(ServerCommandSource source, String mobtype)
|
||||
{
|
||||
Messenger.send(source, SpawnReporter.printEntitiesByType(mobtype, source.getWorld()));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package carpet.fakes;
|
||||
|
||||
public interface PlayerListHudInterface
|
||||
{
|
||||
boolean hasFooterOrHeader();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package carpet.helpers;
|
||||
|
||||
import net.minecraft.entity.LivingEntity LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity PlayerEntity;
|
||||
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(PlayerEntity 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 = LivingEntity.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,28 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.PlayerListHudInterface;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.hud.InGameHud;
|
||||
import net.minecraft.client.gui.hud.PlayerListHud;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(InGameHud.class)
|
||||
public abstract class InGameHudMixin
|
||||
{
|
||||
@Shadow
|
||||
@Final
|
||||
private MinecraftClient client;
|
||||
|
||||
@Shadow @Final private PlayerListHud playerListHud;
|
||||
|
||||
@Redirect(method = "draw", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;isInSingleplayer()Z"))
|
||||
private boolean onDraw(MinecraftClient minecraftClient)
|
||||
{
|
||||
return this.client.isInSingleplayer() && !((PlayerListHudInterface) playerListHud).hasFooterOrHeader();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package carpet.mixins;
|
||||
import carpet.fakes.PlayerListHudInterface;
|
||||
import net.minecraft.client.gui.hud.PlayerListHud;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(PlayerListHud.class)
|
||||
public abstract class PlayerListHudMixin implements PlayerListHudInterface
|
||||
{
|
||||
@Shadow private Component footer;
|
||||
|
||||
@Shadow private Component header;
|
||||
|
||||
public boolean hasFooterOrHeader()
|
||||
{
|
||||
return footer != null || header != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.utils.SpawnReporter;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.level.LevelProperties;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
@Mixin(ServerChunkManager.class)
|
||||
public abstract class ServerChunkManagerMixin
|
||||
{
|
||||
@Shadow @Final private ServerWorld world;
|
||||
|
||||
@Inject(
|
||||
method = "tickChunks",
|
||||
locals = LocalCapture.CAPTURE_FAILHARD,
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/world/ThreadedAnvilChunkStorage;entryIterator()Lit/unimi/dsi/fastutil/objects/ObjectBidirectionalIterator;",
|
||||
shift = At.Shift.AFTER,
|
||||
ordinal = 0
|
||||
|
||||
))
|
||||
private void grabMobcaps(CallbackInfo ci,
|
||||
long long_1,
|
||||
long long_2,
|
||||
LevelProperties levelProperties_1,
|
||||
boolean boolean_1,
|
||||
boolean boolean_2,
|
||||
int int_1,
|
||||
BlockPos blockPos_1,
|
||||
boolean boolean_3,
|
||||
int int_2,
|
||||
EntityCategory[] entityCategorys_1,
|
||||
Object2IntMap object2IntMap_1)
|
||||
{
|
||||
DimensionType dim = this.world.dimension.getType();
|
||||
SpawnReporter.mobCounts.put(dim, (Object2IntMap<EntityCategory>)object2IntMap_1);
|
||||
SpawnReporter.chunkCounts.put(dim, int_2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package carpet.utils;
|
||||
|
||||
import carpet.helpers.HopperCounter;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.logging.LoggerRegistry;
|
||||
import carpet.logging.logHelpers.PacketCounter;
|
||||
import carpet.mixins.PlayerListHeaderS2CPacketMixin;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.client.network.packet.PlayerListHeaderS2CPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.Pair;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.network.chat.BaseComponent;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class HUDController
|
||||
{
|
||||
public static Map<PlayerEntity, List<BaseComponent>> player_huds = new HashMap<>();
|
||||
|
||||
public static void addMessage(PlayerEntity player, BaseComponent hudMessage)
|
||||
{
|
||||
if (!player_huds.containsKey(player))
|
||||
{
|
||||
player_huds.put(player, new ArrayList<>());
|
||||
}
|
||||
else
|
||||
{
|
||||
player_huds.get(player).add(new TextComponent("\n"));
|
||||
}
|
||||
player_huds.get(player).add(hudMessage);
|
||||
}
|
||||
public static void clear_player(PlayerEntity player)
|
||||
{
|
||||
PlayerListHeaderS2CPacket packet = new PlayerListHeaderS2CPacket();
|
||||
((PlayerListHeaderS2CPacketMixin)packet).setHeader(new TextComponent(""));
|
||||
((PlayerListHeaderS2CPacketMixin)packet).setFooter(new TextComponent(""));
|
||||
((ServerPlayerEntity)player).networkHandler.sendPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
public static void update_hud(MinecraftServer server)
|
||||
{
|
||||
if(server.getTicks() % 20 != 0)
|
||||
return;
|
||||
|
||||
player_huds.clear();
|
||||
|
||||
if (LoggerRegistry.__tps)
|
||||
LoggerRegistry.getLogger("tps").log(()-> send_tps_display(server));
|
||||
|
||||
if (LoggerRegistry.__mobcaps)
|
||||
LoggerRegistry.getLogger("mobcaps").log((option, player) -> {
|
||||
DimensionType dim = player.dimension;
|
||||
switch (option)
|
||||
{
|
||||
case "overworld":
|
||||
dim = DimensionType.OVERWORLD;
|
||||
break;
|
||||
case "nether":
|
||||
dim = DimensionType.THE_NETHER;
|
||||
break;
|
||||
case "end":
|
||||
dim = DimensionType.THE_END;
|
||||
break;
|
||||
}
|
||||
return new BaseComponent[]{SpawnReporter.printMobcapsForDimension(dim, false).get(0)};
|
||||
});
|
||||
|
||||
if(LoggerRegistry.__counter)
|
||||
LoggerRegistry.getLogger("counter").log((option)->send_counter_info(server, option));
|
||||
|
||||
if (LoggerRegistry.__packets)
|
||||
LoggerRegistry.getLogger("packets").log(()-> packetCounter());
|
||||
|
||||
for (PlayerEntity player: player_huds.keySet())
|
||||
{
|
||||
PlayerListHeaderS2CPacket packet = new PlayerListHeaderS2CPacket();
|
||||
((PlayerListHeaderS2CPacketMixin)packet).setHeader(new TextComponent(""));
|
||||
((PlayerListHeaderS2CPacketMixin)packet).setFooter(Messenger.c(player_huds.get(player).toArray(new Object[0])));
|
||||
((ServerPlayerEntity)player).networkHandler.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
private static BaseComponent [] send_tps_display(MinecraftServer server)
|
||||
{
|
||||
double MSPT = MathHelper.average(server.lastTickLengths) * 1.0E-6D;
|
||||
double TPS = 1000.0D / Math.max((TickSpeed.time_warp_start_time != 0)?0.0:TickSpeed.mspt, MSPT);
|
||||
String color = Messenger.heatmap_color(MSPT,TickSpeed.mspt);
|
||||
return new BaseComponent[]{Messenger.c(
|
||||
"g TPS: ", String.format(Locale.US, "%s %.1f",color, TPS),
|
||||
"g MSPT: ", String.format(Locale.US,"%s %.1f", color, MSPT))};
|
||||
}
|
||||
|
||||
private static BaseComponent [] send_counter_info(MinecraftServer server, String color)
|
||||
{
|
||||
HopperCounter counter = HopperCounter.getCounter(color);
|
||||
List <BaseComponent> res = counter == null ? Collections.emptyList() : counter.format(server, false, true);
|
||||
return new BaseComponent[]{ Messenger.c(res.toArray(new Object[0]))};
|
||||
}
|
||||
private static BaseComponent [] packetCounter()
|
||||
{
|
||||
BaseComponent [] ret = new BaseComponent[]{
|
||||
Messenger.c("w I/" + PacketCounter.totalIn + " O/" + PacketCounter.totalOut),
|
||||
};
|
||||
PacketCounter.reset();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,8 @@ public class Messenger
|
||||
}
|
||||
public static String heatmap_color(double actual, double reference)
|
||||
{
|
||||
String color = "e";
|
||||
String color = "g";
|
||||
if (actual >= 0.0D) color = "e";
|
||||
if (actual > 0.5D*reference) color = "y";
|
||||
if (actual > 0.8D*reference) color = "r";
|
||||
if (actual > reference) color = "m";
|
||||
|
||||
@@ -0,0 +1,526 @@
|
||||
|
||||
package carpet.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import carpet.mixins.WeightedPickerEntryMixin;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import net.minecraft.entity.SpawnType;
|
||||
import net.minecraft.entity.mob.MobEntity;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.structure.OceanMonumentGenerator;
|
||||
import net.minecraft.util.Pair;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.network.chat.BaseComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.minecraft.util.DyeColor;
|
||||
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.SpawnHelper;
|
||||
import net.minecraft.entity.SpawnRestriction;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
import net.minecraft.entity.passive.OcelotEntity;
|
||||
import net.minecraft.world.Heightmap;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
|
||||
import java.lang.Math;
|
||||
|
||||
public class SpawnReporter
|
||||
{
|
||||
public static String [] mob_groups = {"hostile","passive","water","ambient"};
|
||||
public static boolean mock_spawns = false;
|
||||
|
||||
public static Long track_spawns = 0L;
|
||||
public static final HashMap<DimensionType, Object2IntMap<EntityCategory>> mobCounts = new HashMap<>();
|
||||
public static final HashMap<DimensionType, Integer> chunkCounts = new HashMap<>();
|
||||
|
||||
public static final HashMap<String, HashMap<String,Long>> spawn_stats = new HashMap<String, HashMap<String,Long>>();
|
||||
public static double mobcap_exponent = 0.0D;
|
||||
|
||||
public static final HashMap<String, Long> spawn_attempts = new HashMap<String, Long>();
|
||||
public static final HashMap<String, Long> overall_spawn_ticks = new HashMap<String, Long>();
|
||||
public static final HashMap<String, Long> spawn_ticks_full = new HashMap<String, Long>();
|
||||
public static final HashMap<String, Long> spawn_ticks_fail = new HashMap<String, Long>();
|
||||
public static final HashMap<String, Long> spawn_ticks_succ = new HashMap<String, Long>();
|
||||
public static final HashMap<String, Long> spawn_ticks_spawns = new HashMap<String, Long>();
|
||||
public static final HashMap<String, Long> spawn_cap_count = new HashMap<String, Long>();
|
||||
public static class SpawnPos
|
||||
{
|
||||
public String mob;
|
||||
public BlockPos pos;
|
||||
public SpawnPos(String mob, BlockPos pos)
|
||||
{
|
||||
this.mob = mob;
|
||||
this.pos = pos;
|
||||
}
|
||||
}
|
||||
public static final HashMap<String, EvictingQueue<SpawnPos, Integer>> spawned_mobs = new HashMap<>();
|
||||
public static final HashMap<String, Integer> spawn_tries = new HashMap<>();
|
||||
public static BlockPos lower_spawning_limit = null;
|
||||
public static BlockPos upper_spawning_limit = null;
|
||||
|
||||
static {
|
||||
reset_spawn_stats(true);
|
||||
}
|
||||
|
||||
public static void registerSpawn(LivingEntity el, String type, String mob, BlockPos pos) { registerSpawn(el, type, mob, pos, 1L);}
|
||||
public static void registerSpawn(LivingEntity el, String type, String mob, BlockPos pos, long value)
|
||||
{
|
||||
if (lower_spawning_limit != null)
|
||||
{
|
||||
if (!( (lower_spawning_limit.getX() <= pos.getX() && pos.getX() <= upper_spawning_limit.getX()) &&
|
||||
(lower_spawning_limit.getY() <= pos.getY() && pos.getY() <= upper_spawning_limit.getY()) &&
|
||||
(lower_spawning_limit.getZ() <= pos.getZ() && pos.getZ() <= upper_spawning_limit.getZ())
|
||||
))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long count = spawn_stats.get(type).getOrDefault(mob, 0L);
|
||||
spawn_stats.get(type).put(mob, count + value);
|
||||
spawned_mobs.get(type).put(new SpawnPos(mob, new BlockPos(el)), 1);
|
||||
}
|
||||
|
||||
|
||||
public static List<BaseComponent> printMobcapsForDimension(DimensionType dim, boolean multiline)
|
||||
{
|
||||
String name = dim.toString();
|
||||
List<BaseComponent> lst = new ArrayList<>();
|
||||
if (multiline)
|
||||
lst.add(Messenger.s(String.format("Mobcaps for %s:",name)));
|
||||
Object2IntMap<EntityCategory> dimCounts = mobCounts.get(dim);
|
||||
int chunkcount = chunkCounts.getOrDefault(dim, -1);
|
||||
if (dimCounts == null || chunkcount < 0)
|
||||
{
|
||||
lst.add(Messenger.c("g --UNAVAILABLE--"));
|
||||
return lst;
|
||||
}
|
||||
|
||||
List<String> shortCodes = new ArrayList<>();
|
||||
for (EntityCategory enumcreaturetype : EntityCategory.values())
|
||||
{
|
||||
String type_code = String.format("%s", enumcreaturetype);
|
||||
int cur = dimCounts.getOrDefault(enumcreaturetype, -1);
|
||||
int max = chunkcount * enumcreaturetype.getSpawnCap() / (int)Math.pow(17.0D, 2.0D); // from ServerChunkManager.CHUNKS_ELIGIBLE_FOR_SPAWNING
|
||||
String color = Messenger.heatmap_color(cur, max);
|
||||
String mobColor = Messenger.creatureTypeColor(enumcreaturetype);
|
||||
if (multiline)
|
||||
{
|
||||
int rounds = spawn_tries.get(type_code);
|
||||
lst.add(Messenger.c(String.format("w %s: ", type_code),
|
||||
(cur < 0) ? "g -" : (color + " " + cur), "g / ", mobColor + " " + max,
|
||||
(rounds == 1) ? "w " : String.format("fi (%d rounds/tick)", spawn_tries.get(type_code))
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
shortCodes.add(color+" "+((cur<0)?"-":cur));
|
||||
shortCodes.add("g /");
|
||||
shortCodes.add(mobColor+" "+max);
|
||||
shortCodes.add("g ,");
|
||||
}
|
||||
}
|
||||
if (!multiline)
|
||||
{
|
||||
if (shortCodes.size()>0)
|
||||
{
|
||||
shortCodes.remove(shortCodes.size() - 1);
|
||||
lst.add(Messenger.c(shortCodes.toArray(new Object[0])));
|
||||
}
|
||||
else
|
||||
{
|
||||
lst.add(Messenger.c("g --UNAVAILABLE--"));
|
||||
}
|
||||
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
public static List<BaseComponent> recent_spawns(World world, String creature_type_code)
|
||||
{
|
||||
|
||||
EntityCategory creature_type = get_creature_type_from_code(creature_type_code);
|
||||
List<BaseComponent> lst = new ArrayList<>();
|
||||
if ((track_spawns == 0L))
|
||||
{
|
||||
lst.add(Messenger.s("Spawn tracking not started"));
|
||||
return lst;
|
||||
}
|
||||
if (creature_type == null)
|
||||
{
|
||||
lst.add(Messenger.s(String.format("Incorrect creature type: %s",creature_type_code)));
|
||||
return lst;
|
||||
}
|
||||
|
||||
String type_code = get_type_string(creature_type);
|
||||
|
||||
lst.add(Messenger.s(String.format("Recent %s spawns:",type_code)));
|
||||
for (SpawnPos entry: spawned_mobs.get(type_code).keySet())
|
||||
{
|
||||
lst.add( Messenger.c(String.format("w - %s ",entry.mob), Messenger.tp("wb",entry.pos)));
|
||||
}
|
||||
|
||||
if (lst.size()==1)
|
||||
{
|
||||
lst.add(Messenger.s(" - Nothing spawned yet, sorry."));
|
||||
}
|
||||
return lst;
|
||||
|
||||
}
|
||||
|
||||
public static List<BaseComponent> show_mobcaps(BlockPos pos, World worldIn)
|
||||
{
|
||||
DyeColor under = WoolTool.getWoolColorAtPosition(worldIn, pos.down());
|
||||
if (under == null)
|
||||
{
|
||||
if (track_spawns > 0L)
|
||||
{
|
||||
return tracking_report(worldIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
//return print_general_mobcaps(worldIn);
|
||||
}
|
||||
}
|
||||
String creature_type = get_type_code_from_wool_code(under);
|
||||
if (creature_type != null)
|
||||
{
|
||||
if (track_spawns > 0L)
|
||||
{
|
||||
return recent_spawns(worldIn, creature_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
return printEntitiesByType(creature_type, worldIn);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (track_spawns > 0L)
|
||||
{
|
||||
return tracking_report(worldIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
return tracking_report(worldIn);
|
||||
//// TODO
|
||||
//return print_general_mobcaps(worldIn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String get_type_code_from_wool_code(DyeColor color)
|
||||
{
|
||||
switch (color)
|
||||
{
|
||||
case RED:
|
||||
return "hostile";
|
||||
case GREEN:
|
||||
return "passive";
|
||||
case BLUE:
|
||||
return "water";
|
||||
case BROWN:
|
||||
return "ambient";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static EntityCategory get_creature_type_from_code(String type_code)
|
||||
{
|
||||
if ("hostile".equalsIgnoreCase(type_code))
|
||||
{
|
||||
return EntityCategory.MONSTER;
|
||||
}
|
||||
else if ("passive".equalsIgnoreCase(type_code))
|
||||
{
|
||||
return EntityCategory.CREATURE;
|
||||
}
|
||||
else if ("water".equalsIgnoreCase(type_code))
|
||||
{
|
||||
return EntityCategory.WATER_CREATURE;
|
||||
}
|
||||
else if ("ambient".equalsIgnoreCase(type_code))
|
||||
{
|
||||
return EntityCategory.AMBIENT;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static String get_type_string(EntityCategory type)
|
||||
{
|
||||
return String.format("%s", type);
|
||||
}
|
||||
|
||||
public static String get_creature_code_from_string(String str)
|
||||
{
|
||||
return get_type_string(get_creature_type_from_code(str));
|
||||
}
|
||||
|
||||
public static List<BaseComponent> printEntitiesByType(String creature_type_code, World worldIn) //Class<?> entityType)
|
||||
{
|
||||
EntityCategory typ = get_creature_type_from_code(creature_type_code);
|
||||
List<BaseComponent> lst = new ArrayList<>();
|
||||
if (typ == null)
|
||||
{
|
||||
lst.add(Messenger.c(String.format("r Incorrect creature type: %s",creature_type_code)));
|
||||
return lst;
|
||||
}
|
||||
Class<?> cls = typ.getDeclaringClass();// getBaseClass();// getCreatureClass();
|
||||
lst.add( Messenger.s(String.format("Loaded entities for %s class:", get_type_string(typ))));
|
||||
|
||||
lst.add(Messenger.s(" <sorry, this list will be populated, once M figures out what counts>"));
|
||||
//for (Entity entity : ((ServerWorld)worldIn).getEntities(...))
|
||||
//{
|
||||
// if ((!(entity instanceof LivingEntity) || !((LivingEntity)entity).isNoDespawnRequired()) && cls.isAssignableFrom(entity.getClass()))
|
||||
// {
|
||||
// lst.add(Messenger.c("w - ",
|
||||
// Messenger.tp("w", entity.posX, entity.posY, entity.posZ),"w : "+EntityType.getId(entity.getType())));
|
||||
// }
|
||||
//}
|
||||
if (lst.size()==1)
|
||||
{
|
||||
lst.add(Messenger.s(" - Empty."));
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
public static void initialize_mocking()
|
||||
{
|
||||
reset_spawn_stats(false);
|
||||
mock_spawns = true;
|
||||
|
||||
}
|
||||
public static void stop_mocking()
|
||||
{
|
||||
reset_spawn_stats(false);
|
||||
mock_spawns = false;
|
||||
|
||||
}
|
||||
public static void reset_spawn_stats(boolean full)
|
||||
{
|
||||
spawn_stats.clear();
|
||||
spawned_mobs.clear();
|
||||
for (EntityCategory enumcreaturetype : EntityCategory.values())
|
||||
{
|
||||
String type_code = String.format("%s", enumcreaturetype);
|
||||
if (full)
|
||||
{
|
||||
spawn_tries.put(type_code, 1);
|
||||
}
|
||||
for (String suffix: new String[] {""," (N)"," (E)"})
|
||||
{
|
||||
String code = type_code+suffix;
|
||||
overall_spawn_ticks.put(code, 0L);
|
||||
spawn_attempts.put(code, 0L);
|
||||
spawn_ticks_full.put(code, 0L);
|
||||
spawn_ticks_fail.put(code, 0L);
|
||||
spawn_ticks_succ.put(code, 0L);
|
||||
spawn_ticks_spawns.put(code, 0L);
|
||||
spawn_cap_count.put(code, 0L);
|
||||
}
|
||||
|
||||
spawn_stats.put(type_code, new HashMap<>());
|
||||
spawned_mobs.put(type_code, new EvictingQueue<>());
|
||||
}
|
||||
track_spawns = 0L;
|
||||
}
|
||||
|
||||
public static List<BaseComponent> tracking_report(World worldIn)
|
||||
{
|
||||
List<BaseComponent> report = new ArrayList<>();
|
||||
if (track_spawns == 0L)
|
||||
{
|
||||
report.add(Messenger.c(
|
||||
"w Spawn tracking disabled, type '",
|
||||
"wi /spawn tracking start","/spawn tracking start",
|
||||
"w ' to enable"));
|
||||
return report;
|
||||
}
|
||||
Long duration = (long) worldIn.getServer().getTicks() - track_spawns;
|
||||
report.add(Messenger.c("bw --------------------"));
|
||||
String simulated = mock_spawns?"[SIMULATED] ":"";
|
||||
String location = (lower_spawning_limit != null)?String.format("[in (%d, %d, %d)x(%d, %d, %d)]",
|
||||
lower_spawning_limit.getX(),lower_spawning_limit.getY(),lower_spawning_limit.getZ(),
|
||||
upper_spawning_limit.getX(),upper_spawning_limit.getY(),upper_spawning_limit.getZ() ):"";
|
||||
report.add(Messenger.s(String.format("%sSpawn statistics %s: for %.1f min", simulated, location, (duration/72000.0)*60)));
|
||||
for (EntityCategory enumcreaturetype : EntityCategory.values())
|
||||
{
|
||||
String type_code = String.format("%s", enumcreaturetype);
|
||||
boolean there_are_mobs_to_list = false;
|
||||
for (String world_code: new String[] {"", " (N)", " (E)"})
|
||||
{
|
||||
String code = type_code+world_code;
|
||||
if (spawn_ticks_spawns.get(code) > 0L)
|
||||
{
|
||||
there_are_mobs_to_list = true;
|
||||
double hours = overall_spawn_ticks.get(code)/72000.0;
|
||||
report.add(Messenger.s(String.format(" > %s (%.1f min), %.1f m/t, {%.1f%%F / %.1f%%- / %.1f%%+}; %.2f s/att",
|
||||
code,
|
||||
60*hours,
|
||||
(1.0D*spawn_cap_count.get(code))/ spawn_attempts.get(code),
|
||||
(100.0D*spawn_ticks_full.get(code))/ spawn_attempts.get(code),
|
||||
(100.0D*spawn_ticks_fail.get(code))/ spawn_attempts.get(code),
|
||||
(100.0D*spawn_ticks_succ.get(code))/ spawn_attempts.get(code),
|
||||
(1.0D*spawn_ticks_spawns.get(code))/(spawn_ticks_fail.get(code)+spawn_ticks_succ.get(code))
|
||||
)));
|
||||
}
|
||||
}
|
||||
if (there_are_mobs_to_list)
|
||||
{
|
||||
for (String creature_name : spawn_stats.get(type_code).keySet())
|
||||
{
|
||||
report.add(Messenger.s(String.format(" - %s: %d spawns, %d per hour",
|
||||
creature_name,
|
||||
spawn_stats.get(type_code).get(creature_name),
|
||||
(72000 * spawn_stats.get(type_code).get(creature_name)/duration ))));
|
||||
}
|
||||
}
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void killEntity(LivingEntity entity)
|
||||
{
|
||||
if (entity.hasVehicle())
|
||||
{
|
||||
entity.getVehicle().remove();
|
||||
}
|
||||
if (entity.hasPassengers())
|
||||
{
|
||||
for (Entity e: entity.getPassengerList())
|
||||
{
|
||||
e.remove();
|
||||
}
|
||||
}
|
||||
if (entity instanceof OcelotEntity)
|
||||
{
|
||||
for (Entity e: entity.getEntityWorld().getEntities(OcelotEntity.class, entity.getBoundingBox()))
|
||||
{
|
||||
e.remove();
|
||||
}
|
||||
}
|
||||
entity.remove();
|
||||
}
|
||||
|
||||
public static List<BaseComponent> report(BlockPos pos, World worldIn)
|
||||
{
|
||||
List<BaseComponent> rep = new ArrayList<>();
|
||||
int x = pos.getX(); int y = pos.getY(); int z = pos.getZ();
|
||||
Chunk chunk = worldIn.getChunk(pos);
|
||||
int lc = chunk.sampleHeightmap(Heightmap.Type.WORLD_SURFACE, x, z) + 1;
|
||||
String where = String.format((y >= lc) ? "%d blocks above it." : "%d blocks below it.", MathHelper.abs(y-lc));
|
||||
if (y == lc) where = "right at it.";
|
||||
rep.add(Messenger.s(String.format("Maximum spawn Y value for (%+d, %+d) is %d. You are "+where, x, z, lc )));
|
||||
rep.add(Messenger.s("Spawns:"));
|
||||
for (EntityCategory enumcreaturetype : EntityCategory.values())
|
||||
{
|
||||
String type_code = String.format("%s", enumcreaturetype).substring(0, 3);
|
||||
List<Biome.SpawnEntry> lst = ((ChunkGenerator)worldIn.getChunkManager().getChunkGenerator()).getEntitySpawnList(enumcreaturetype, pos);
|
||||
if (lst != null && !lst.isEmpty())
|
||||
{
|
||||
for (Biome.SpawnEntry spawnEntry : lst)
|
||||
{
|
||||
boolean canspawn = SpawnHelper.canSpawn(SpawnRestriction.getLocation(spawnEntry.type), worldIn, pos, spawnEntry.type);
|
||||
int will_spawn = -1;
|
||||
boolean fits = false;
|
||||
boolean fits1 = false;
|
||||
|
||||
MobEntity mob;
|
||||
try
|
||||
{
|
||||
mob = (MobEntity) spawnEntry.type.create(worldIn);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
exception.printStackTrace();
|
||||
return rep;
|
||||
}
|
||||
|
||||
boolean fits_true = false;
|
||||
boolean fits_false = false;
|
||||
|
||||
if (canspawn)
|
||||
{
|
||||
will_spawn = 0;
|
||||
for (int attempt = 0; attempt < 50; ++attempt)
|
||||
{
|
||||
float f = (float)x + 0.5F;
|
||||
float f1 = (float)z + 0.5F;
|
||||
mob.setPositionAndAngles((double)f, (double)y, (double)f1, worldIn.random.nextFloat() * 360.0F, 0.0F);
|
||||
fits1 = worldIn.doesNotCollide(mob);
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
if (mob.canSpawn(worldIn, SpawnType.NATURAL))
|
||||
{
|
||||
will_spawn += 1;
|
||||
}
|
||||
}
|
||||
mob.initialize(worldIn, worldIn.getLocalDifficulty(new BlockPos(mob)), SpawnType.NATURAL, null, null);
|
||||
// the code invokes onInitialSpawn after getCanSpawHere
|
||||
fits = fits1 && worldIn.doesNotCollide(mob);
|
||||
if (fits)
|
||||
{
|
||||
fits_true = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fits_false = true;
|
||||
}
|
||||
|
||||
killEntity(mob);
|
||||
|
||||
try
|
||||
{
|
||||
mob = (MobEntity) spawnEntry.type.create(worldIn);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
exception.printStackTrace();
|
||||
return rep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String creature_name = Registry.ENTITY_TYPE.getId(mob.getType()).toString().replaceFirst("minecraft:","");
|
||||
String pack_size = String.format("%d", mob.getLimitPerChunk());//String.format("%d-%d", animal.minGroupCount, animal.maxGroupCount);
|
||||
int weight = ((WeightedPickerEntryMixin) spawnEntry).getWeight();
|
||||
if (canspawn)
|
||||
{
|
||||
String c = (fits_true && will_spawn>0)?"e":"gi";
|
||||
rep.add(Messenger.c(
|
||||
String.format("%s %s: %s (%d:%d-%d/%d), can: ",c,type_code,creature_name,weight,spawnEntry.minGroupSize, spawnEntry.maxGroupSize, mob.getLimitPerChunk()),
|
||||
"l YES",
|
||||
c+" , fit: ",
|
||||
((fits_true && fits_false)?"y YES and NO":(fits_true?"l YES":"r NO")),
|
||||
c+" , will: ",
|
||||
((will_spawn>0)?"l ":"r ")+Math.round((double)will_spawn)/10+"%"
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
rep.add(Messenger.c(String.format("gi %s: %s (%d:%d-%d/%d), can: ",type_code,creature_name,weight,spawnEntry.minGroupSize, spawnEntry.maxGroupSize, mob.getLimitPerChunk()), "n NO"));
|
||||
}
|
||||
killEntity(mob);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rep;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user