2023-03-15 21:52:13 +00:00
|
|
|
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2021-06-05 09:09:28 +00:00
|
|
|
package dan200.computercraft.shared;
|
|
|
|
|
2022-11-09 20:10:24 +00:00
|
|
|
import dan200.computercraft.api.ComputerCraftAPI;
|
Add config options for a global bandwidth limit
This uses Netty's global traffic shaping handlers to limit the rate at
which packets can be sent and received. If the bandwidth limit is hit,
we'll start dropping packets, which will mean remote servers send
traffic to us at a much slower pace.
This isn't perfect, as there is only a global limit, and not a
per-computer one. As a result, its possible for one computer to use
all/most bandwidth, and thus slow down other computers.
This would be something to improve on in the future. However, I've spent
a lot of time reading the netty source code and docs, and the
implementation for that is significantly more complex, and one I'm not
comfortable working on right now.
For the time being, this satisfies the issues in #33 and hopefully
alleviates server owner's concerns about the http API. Remaining
problems can either be solved by moderation (with help of the
//computercraft track` command) or future updates.
Closes #33
2021-07-28 14:53:22 +00:00
|
|
|
import dan200.computercraft.core.apis.http.NetworkUtils;
|
2022-11-09 23:58:56 +00:00
|
|
|
import dan200.computercraft.impl.PocketUpgrades;
|
|
|
|
import dan200.computercraft.impl.TurtleUpgrades;
|
2022-10-30 09:20:26 +00:00
|
|
|
import dan200.computercraft.shared.computer.core.ResourceMount;
|
2022-10-21 20:01:01 +00:00
|
|
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
2022-10-22 00:35:04 +00:00
|
|
|
import dan200.computercraft.shared.computer.metrics.ComputerMBean;
|
2022-11-08 10:46:09 +00:00
|
|
|
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
|
|
|
|
import dan200.computercraft.shared.util.DropConsumer;
|
|
|
|
import dan200.computercraft.shared.util.TickScheduler;
|
2024-04-25 20:32:48 +00:00
|
|
|
import net.minecraft.core.registries.Registries;
|
2024-01-03 18:42:31 +00:00
|
|
|
import net.minecraft.resources.ResourceKey;
|
2021-08-03 20:46:53 +00:00
|
|
|
import net.minecraft.resources.ResourceLocation;
|
2022-11-08 10:46:09 +00:00
|
|
|
import net.minecraft.server.MinecraftServer;
|
2021-06-05 10:36:08 +00:00
|
|
|
import net.minecraft.server.dedicated.DedicatedServer;
|
Reschedule block entities when chunks are loaded
Minecraft sometimes keeps chunks in-memory, but not actively loaded. If
we schedule a block entity to be ticked and that chunk is is then
transitioned to this partially-loaded state, then the block entity is
never actually ticked.
This is most visible with monitors. When a monitor's contents changes,
if the monitor is not already marked as changed, we set it as changed
and schedule a tick (see ServerMonitor). However, if the tick is
dropped, we don't clear the changed flag, meaning subsequent changes
don't requeue the monitor to be ticked, and so the monitor is never
updated.
We fix this by maintaining a list of block entities whose tick was
dropped. If these block entities (or rather their owning chunk) is ever
re-loaded, then we reschedule them to be ticked.
An alternative approach here would be to add the scheduled tick directly
to the LevelChunk. However, getting hold of the LevelChunk for unloaded
blocks is quiet nasty, so I think best avoided.
Fixes #1146. Fixes #1560 - I believe the second one is a duplicate, and
I noticed too late :D.
2024-02-26 19:01:25 +00:00
|
|
|
import net.minecraft.server.level.ServerLevel;
|
2022-11-08 10:46:09 +00:00
|
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
|
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
|
|
|
import net.minecraft.world.entity.Entity;
|
2024-01-03 18:42:31 +00:00
|
|
|
import net.minecraft.world.item.CreativeModeTab;
|
|
|
|
import net.minecraft.world.item.CreativeModeTabs;
|
2022-11-08 10:46:09 +00:00
|
|
|
import net.minecraft.world.item.ItemStack;
|
|
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
2021-08-03 20:46:53 +00:00
|
|
|
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
|
2023-07-07 09:18:16 +00:00
|
|
|
import net.minecraft.world.level.storage.loot.LootPool;
|
2024-04-25 20:32:48 +00:00
|
|
|
import net.minecraft.world.level.storage.loot.LootTable;
|
|
|
|
import net.minecraft.world.level.storage.loot.entries.NestedLootTable;
|
2023-07-07 09:18:16 +00:00
|
|
|
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
2021-06-05 09:09:28 +00:00
|
|
|
|
2023-07-07 09:18:16 +00:00
|
|
|
import javax.annotation.Nullable;
|
2021-06-05 09:09:28 +00:00
|
|
|
import java.util.Set;
|
2022-11-08 10:46:09 +00:00
|
|
|
import java.util.function.BiConsumer;
|
2021-06-05 09:09:28 +00:00
|
|
|
|
|
|
|
/**
|
2022-11-08 10:46:09 +00:00
|
|
|
* Event listeners for server/common code.
|
2022-10-25 18:17:55 +00:00
|
|
|
* <p>
|
2022-11-08 10:46:09 +00:00
|
|
|
* All event handlers should be defined in this class, and then invoked from a loader-specific event handler. This means
|
|
|
|
* it's much easier to ensure that each hook is called in all loader source sets.
|
2021-06-05 09:09:28 +00:00
|
|
|
*/
|
|
|
|
public final class CommonHooks {
|
|
|
|
private CommonHooks() {
|
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void onServerTickStart(MinecraftServer server) {
|
|
|
|
ServerContext.get(server).tick();
|
|
|
|
TickScheduler.tick();
|
2021-06-05 09:09:28 +00:00
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void onServerTickEnd() {
|
|
|
|
MonitorWatcher.onTick();
|
2021-06-05 10:36:08 +00:00
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void onServerStarting(MinecraftServer server) {
|
2021-11-28 15:58:30 +00:00
|
|
|
if (server instanceof DedicatedServer dediServer && dediServer.getProperties().enableJmxMonitoring) {
|
2021-06-05 10:36:08 +00:00
|
|
|
ComputerMBean.register();
|
|
|
|
}
|
2021-06-05 09:09:28 +00:00
|
|
|
|
2022-05-04 10:31:39 +00:00
|
|
|
resetState();
|
2022-10-21 20:01:01 +00:00
|
|
|
ServerContext.create(server);
|
2022-10-22 00:35:04 +00:00
|
|
|
ComputerMBean.start(server);
|
2021-06-05 09:09:28 +00:00
|
|
|
}
|
|
|
|
|
2024-01-31 20:55:14 +00:00
|
|
|
public static void onServerStarted(MinecraftServer server) {
|
|
|
|
// ItemDetails requires creative tabs to be populated, however by default this is done lazily on the client and
|
|
|
|
// not at all on the server! We instead do this once on server startup.
|
|
|
|
CreativeModeTabs.tryRebuildTabContents(server.getWorldData().enabledFeatures(), false, server.registryAccess());
|
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void onServerStopped() {
|
2022-05-04 10:31:39 +00:00
|
|
|
resetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void resetState() {
|
2022-10-21 20:01:01 +00:00
|
|
|
ServerContext.close();
|
Add config options for a global bandwidth limit
This uses Netty's global traffic shaping handlers to limit the rate at
which packets can be sent and received. If the bandwidth limit is hit,
we'll start dropping packets, which will mean remote servers send
traffic to us at a much slower pace.
This isn't perfect, as there is only a global limit, and not a
per-computer one. As a result, its possible for one computer to use
all/most bandwidth, and thus slow down other computers.
This would be something to improve on in the future. However, I've spent
a lot of time reading the netty source code and docs, and the
implementation for that is significantly more complex, and one I'm not
comfortable working on right now.
For the time being, this satisfies the issues in #33 and hopefully
alleviates server owner's concerns about the http API. Remaining
problems can either be solved by moderation (with help of the
//computercraft track` command) or future updates.
Closes #33
2021-07-28 14:53:22 +00:00
|
|
|
NetworkUtils.reset();
|
2021-06-05 09:09:28 +00:00
|
|
|
}
|
|
|
|
|
Reschedule block entities when chunks are loaded
Minecraft sometimes keeps chunks in-memory, but not actively loaded. If
we schedule a block entity to be ticked and that chunk is is then
transitioned to this partially-loaded state, then the block entity is
never actually ticked.
This is most visible with monitors. When a monitor's contents changes,
if the monitor is not already marked as changed, we set it as changed
and schedule a tick (see ServerMonitor). However, if the tick is
dropped, we don't clear the changed flag, meaning subsequent changes
don't requeue the monitor to be ticked, and so the monitor is never
updated.
We fix this by maintaining a list of block entities whose tick was
dropped. If these block entities (or rather their owning chunk) is ever
re-loaded, then we reschedule them to be ticked.
An alternative approach here would be to add the scheduled tick directly
to the LevelChunk. However, getting hold of the LevelChunk for unloaded
blocks is quiet nasty, so I think best avoided.
Fixes #1146. Fixes #1560 - I believe the second one is a duplicate, and
I noticed too late :D.
2024-02-26 19:01:25 +00:00
|
|
|
public static void onServerChunkUnload(LevelChunk chunk) {
|
|
|
|
if (!(chunk.getLevel() instanceof ServerLevel)) throw new IllegalArgumentException("Not a server chunk.");
|
|
|
|
TickScheduler.onChunkUnload(chunk);
|
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void onChunkWatch(LevelChunk chunk, ServerPlayer player) {
|
|
|
|
MonitorWatcher.onWatch(chunk, player);
|
|
|
|
}
|
|
|
|
|
Reschedule block entities when chunks are loaded
Minecraft sometimes keeps chunks in-memory, but not actively loaded. If
we schedule a block entity to be ticked and that chunk is is then
transitioned to this partially-loaded state, then the block entity is
never actually ticked.
This is most visible with monitors. When a monitor's contents changes,
if the monitor is not already marked as changed, we set it as changed
and schedule a tick (see ServerMonitor). However, if the tick is
dropped, we don't clear the changed flag, meaning subsequent changes
don't requeue the monitor to be ticked, and so the monitor is never
updated.
We fix this by maintaining a list of block entities whose tick was
dropped. If these block entities (or rather their owning chunk) is ever
re-loaded, then we reschedule them to be ticked.
An alternative approach here would be to add the scheduled tick directly
to the LevelChunk. However, getting hold of the LevelChunk for unloaded
blocks is quiet nasty, so I think best avoided.
Fixes #1146. Fixes #1560 - I believe the second one is a duplicate, and
I noticed too late :D.
2024-02-26 19:01:25 +00:00
|
|
|
public static void onChunkTicketLevelChanged(ServerLevel level, long chunkPos, int oldLevel, int newLevel) {
|
|
|
|
TickScheduler.onChunkTicketChanged(level, chunkPos, oldLevel, newLevel);
|
|
|
|
}
|
|
|
|
|
2024-04-25 20:32:48 +00:00
|
|
|
public static final ResourceKey<LootTable> TREASURE_DISK_LOOT = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk"));
|
2021-06-05 09:09:28 +00:00
|
|
|
|
2024-04-25 20:32:48 +00:00
|
|
|
private static final Set<ResourceKey<LootTable>> TREASURE_DISK_LOOT_TABLES = Set.of(
|
2021-08-03 20:46:53 +00:00
|
|
|
BuiltInLootTables.SIMPLE_DUNGEON,
|
|
|
|
BuiltInLootTables.ABANDONED_MINESHAFT,
|
|
|
|
BuiltInLootTables.STRONGHOLD_CORRIDOR,
|
|
|
|
BuiltInLootTables.STRONGHOLD_CROSSING,
|
|
|
|
BuiltInLootTables.STRONGHOLD_LIBRARY,
|
|
|
|
BuiltInLootTables.DESERT_PYRAMID,
|
|
|
|
BuiltInLootTables.JUNGLE_TEMPLE,
|
|
|
|
BuiltInLootTables.IGLOO_CHEST,
|
|
|
|
BuiltInLootTables.WOODLAND_MANSION,
|
|
|
|
BuiltInLootTables.VILLAGE_CARTOGRAPHER
|
2023-06-08 08:48:37 +00:00
|
|
|
);
|
2021-11-24 19:07:12 +00:00
|
|
|
|
2024-04-25 20:32:48 +00:00
|
|
|
public static @Nullable LootPool.Builder getExtraLootPool(ResourceKey<LootTable> lootTable) {
|
|
|
|
if (!TREASURE_DISK_LOOT_TABLES.contains(lootTable)) {
|
2023-07-07 09:18:16 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LootPool.lootPool()
|
2024-04-25 20:32:48 +00:00
|
|
|
.add(NestedLootTable.lootTableReference(TREASURE_DISK_LOOT))
|
2023-07-07 09:18:16 +00:00
|
|
|
.setRolls(ConstantValue.exactly(1));
|
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static void onDatapackReload(BiConsumer<String, PreparableReloadListener> addReload) {
|
|
|
|
addReload.accept("mounts", ResourceMount.RELOAD_LISTENER);
|
|
|
|
addReload.accept("turtle_upgrades", TurtleUpgrades.instance());
|
|
|
|
addReload.accept("pocket_upgrades", PocketUpgrades.instance());
|
Rewrite turtle upgrade registration to be more data driven (#967)
The feature nobody asked for, but we're getting anyway.
Old way to register a turtle/pocket computer upgrade:
ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade")));
New way to register a turtle/pocket computer upgrade:
First, define a serialiser for your turtle upgrade type:
static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
SERIALISERS.register(bus); // Call in your mod constructor.
Now either create a JSON string or use a data generator to register your upgrades:
class TurtleDataGenerator extends TurtleUpgradeDataProvider {
@Override
protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade )
simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade);
}
}
See much better! In all seriousness, this does offer some benefits,
namely that it's now possible to overwrite or create upgrades via
datapacks.
Actual changes:
- Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions.
- Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used
to load upgrades from JSON files in datapacks, and then read/write
them to network packets (much like recipe serialisers).
- The upgrade registries now subscribe to datapack reload events. They
find all JSON files in the
data/$mod_id/computercraft/{turtle,pocket}_upgrades directories,
parse them, and then register them as upgrades.
Once datapacks have fully reloaded, these upgrades are then sent over
the network to the client.
- Add data generators for turtle and pocket computer upgrades, to make
the creation of JSON files a bit easier.
- Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
|
|
|
}
|
|
|
|
|
2022-11-08 10:46:09 +00:00
|
|
|
public static boolean onEntitySpawn(Entity entity) {
|
|
|
|
return DropConsumer.onEntitySpawn(entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean onLivingDrop(Entity entity, ItemStack stack) {
|
|
|
|
return DropConsumer.onLivingDrop(entity, stack);
|
2021-11-24 19:07:12 +00:00
|
|
|
}
|
2024-01-03 18:42:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Add items to an existing creative tab.
|
|
|
|
*
|
|
|
|
* @param key The {@link ResourceKey} for this creative tab.
|
|
|
|
* @param context Additional parameters used for building the contents.
|
|
|
|
* @param out The creative tab output to append items to.
|
|
|
|
*/
|
|
|
|
public static void onBuildCreativeTab(ResourceKey<CreativeModeTab> key, CreativeModeTab.ItemDisplayParameters context, CreativeModeTab.Output out) {
|
|
|
|
if (key == CreativeModeTabs.OP_BLOCKS && context.hasPermissions()) {
|
|
|
|
out.accept(ModRegistry.Items.COMPUTER_COMMAND.get());
|
|
|
|
}
|
|
|
|
}
|
2021-06-05 09:09:28 +00:00
|
|
|
}
|