Compare commits

..

89 Commits

Author SHA1 Message Date
a65cb6f261 stuff 2026-06-06 13:27:03 +02:00
d5202067d1 Add 1.21.1-neoforge, still doesn't work though 2026-06-06 13:00:36 +02:00
a362658371 just in case i fuck everything up 2026-06-06 12:56:01 +02:00
97ed29fc23 changed something. 2026-05-20 20:18:22 +02:00
1c03653fee todo slop 2026-05-19 21:58:35 +02:00
c016cebb0f accuracy slop 2026-05-19 21:51:20 +02:00
885f594ca0 dutch translation but it sucks 2026-05-19 21:46:52 +02:00
45831b780a FRENCH ew 2026-05-19 21:42:16 +02:00
6b0c4468ec FRENCH 2026-05-19 21:37:51 +02:00
d15b34939e models and shi 2026-05-08 03:47:03 +03:00
3f95944314 rack stateslop 2026-05-07 22:43:42 +02:00
21493fec04 add proper container and do some syncy stuff and save 2026-05-07 00:30:08 +02:00
42331390e7 clicky 2026-05-06 17:00:31 +02:00
cdb98bd85e all rack rendering (afaik) and basic right click logic stuff and refactors nobody asked for 2026-05-05 21:39:40 +02:00
7f58fdf55b server hit stuff 2026-05-03 19:06:37 +02:00
17bc614eb9 textures 2026-05-03 18:24:07 +02:00
b78cc44d89 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-05-03 17:21:34 +02:00
4d37483057 render a singular server 2026-05-03 17:21:24 +02:00
4d84ec2ed4 todo stuff 2026-05-03 16:04:38 +03:00
5249832bd6 work on optimized networking 2026-05-02 23:28:02 +03:00
e9885940e2 robot stuff wip i hate minecraft 2026-05-02 22:01:49 +02:00
c63912c1f3 fix 2026-05-02 09:00:51 +02:00
323d213291 render robot model with blockentrenderer 2026-05-01 23:25:22 +02:00
b8968942f8 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-05-01 00:27:16 +02:00
0eee9f5163 robot model 2026-05-01 00:27:09 +02:00
9fd6ea8881 transistor and some random card textures 2026-04-30 22:20:24 +02:00
c6fc9ed9e9 made relay no longer violate physics 2026-04-30 18:14:21 +03:00
9ea3727dfb cables are fully cabling now 2026-04-30 17:47:30 +03:00
21516ee923 FIXED THE EAST BUG! WE ARE SO BACK!! 2026-04-30 07:18:26 +03:00
d8e79d8719 almost fully fixing cables
fixes 1 edge-case, but the east bug is still goofed
2026-04-30 00:50:12 +03:00
5dbd0b8734 bugfix 2026-04-30 00:04:20 +03:00
710547a014 todo: stuff 2026-04-29 21:58:43 +03:00
424f77ba10 relay renderer go brrr 2026-04-29 14:20:45 +03:00
02c0c5d60c relay bugfix and graphics 2026-04-29 14:15:26 +03:00
0b33eda8a8 oopsies 2026-04-29 12:22:57 +03:00
cd909dbd80 half the todo is done 2026-04-28 23:21:34 +03:00
8fb4921e30 cases can do stuff now 2026-04-28 21:35:58 +03:00
86b3486622 fixed the worst oversight of mankind 2026-04-28 21:34:18 +03:00
9a733e0a81 fixed a fixable unfixed problem 2026-04-28 21:14:16 +03:00
740318c020 linguistics or smth idfk 2026-04-28 21:00:34 +03:00
149cfec3ed got rid of some warnings 2026-04-28 20:52:22 +03:00
4d2cb14e45 Merge remote-tracking branch 'origin/main' 2026-04-28 20:50:19 +03:00
7c27955c14 relay blockstates and capacitor magic 2026-04-28 20:50:14 +03:00
71899c0013 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-04-28 19:38:35 +02:00
ced17282f9 new relay textures 2026-04-28 19:38:29 +02:00
a57fca82e5 Merge remote-tracking branch 'origin/main' 2026-04-28 20:32:16 +03:00
3b7e2b02b6 experimental relay bullshit 2026-04-28 20:32:11 +03:00
c968b97faf added oc relay textures 2026-04-28 19:29:42 +02:00
6ef6826483 fuck you have this 2026-04-28 17:13:31 +02:00
ba826b47a8 fuck you have this 2026-04-28 17:05:25 +02:00
02114fc02a reduced network bill by 99.99999% trust 2026-04-28 17:57:48 +03:00
9ac53a0f0e atom how did you miss another one 2026-04-28 16:18:19 +02:00
1f1a456f1d damn you atom 2026-04-28 16:17:31 +02:00
43255b1caf NodeBlockEntity is dead, long live SingleDeviceBlockEntity + cables function 2026-04-28 17:16:33 +03:00
464584877c block textures :) 2026-04-28 15:15:29 +02:00
79053b277a textures 2026-04-28 15:07:55 +02:00
80130a68e1 rip capacitors (WIP refactor)
if anyone touches this while I'm working on it, I will find you, and I will refactor you too
2026-04-27 19:04:26 +03:00
fc8a4aaa58 linguistics 2026-04-27 17:42:02 +03:00
cb380880e4 optimize cable and fix 2026-04-27 11:45:28 +02:00
26ec989a4c Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-04-27 11:21:17 +02:00
883833351c cable stuff 2026-04-27 11:20:22 +02:00
400a773a04 get out of here, missing texture 2026-04-27 01:30:44 +03:00
6532fea543 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-04-26 23:32:04 +02:00
b39d016feb cable model 2026-04-26 23:31:19 +02:00
477abfd80b we got bullshit todo 2026-04-26 17:45:10 +00:00
8edd36124b clean code, acceptable performance 2026-04-26 16:05:46 +00:00
ed6ddddf98 le screen is le synchronized 2026-04-26 06:20:02 +00:00
83749bf3ba CPU no longer boils 2026-04-25 22:37:23 +00:00
f3280f83d1 better stuff 2026-04-25 22:30:31 +00:00
88ffaf7a22 Trăiască România! 2026-04-25 21:43:13 +00:00
8004658a0a get internationalized 2026-04-25 20:58:13 +00:00
a8b45c6565 I plead oopsie daisy 2026-04-25 19:32:33 +00:00
ab0a484021 merged 2026-04-25 19:21:30 +00:00
f9ddc36611 madness of men 2026-04-25 19:19:40 +00:00
a591c9da6f separate cardinal directions from up and down 2026-04-25 13:35:34 +02:00
471921c774 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-04-24 15:07:17 +02:00
cbeb29eeb3 CaseEntity renderer 2026-04-24 15:07:12 +02:00
40f3ce9083 case item model 2026-04-24 10:34:03 +00:00
3bfc07c392 screen progress 2026-04-23 20:23:52 +02:00
a8ca4b1e35 follow advice and fix tiny thnigs 2026-04-23 10:14:27 +02:00
8ccdb76829 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-04-23 10:08:04 +02:00
cb7be27464 fix screen entity renderer 2026-04-23 10:07:58 +02:00
6d20d204a6 case model 2026-04-23 00:52:59 +02:00
b6afa13e72 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-04-22 22:34:33 +02:00
66097fdd6c fixes to node ticking and beeping 2026-04-22 22:30:37 +02:00
55f30283c3 Render screen texture on block 2026-04-22 22:30:23 +02:00
c98ec2fc99 more work on computer stuff 2026-04-22 18:02:45 +02:00
faa1be7175 Merge branch 'main' of https://gitea.codersquack.nl/NeoFlock/NeoComputers 2026-04-22 17:57:09 +02:00
mewhenthe
a14d3a2b73 screen directioned 2026-04-21 20:07:59 +02:00
297 changed files with 4902 additions and 1004 deletions

35
TODO.md Normal file
View File

@@ -0,0 +1,35 @@
# Networking
> All that is left is optimization
## Mergeable device nodes
For optimization of screen grouping and cables, we should add a system that allows merging nodes
## Copy the networking optimizations of OC
> https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/src/main/scala/li/cil/oc/server/network/Network.scala
We should implement this, as it not only makes behavior match better, but also optimizes
it a lot. We do need to replace the NEIGHBORS rule with implementing the SOME visibility.
A direct port is not needed, just taking heavy inspiration from OC.
## Optimize power balancing
Use a smarter algorithm to prevent N storage nodes from iterating N^2 nodes each tick
to balance power.
# Computation
> Pretty important for a computer mod
## JNI
We need the JNI system so we can salvage our hard labor thrown into NeoNucleus.
Also because it is a capable engine and has a good API for architectures, and NCL is very capable.
## Worker threads
Computers need worker threads for running non-synchronized code, because otherwise we're cooked
## Entities as machines
Aside from blocks like cases and robots, we should also support entities like drones.
Not only for OC parity, but also as addons would def love that.

View File

@@ -26,9 +26,16 @@ base {
archivesName.set("${mod.id}-$loader")
}
//val foid = ["forge", "fabric", "neoforge"]
//architectury.common(foid)
//architectury.common(["forge", "fabric"])
//println(stonecutter.current.project.split('-').subList(1, 2))
//architectury.common(stonecutter.current.project.split('-').subList(1, 2))
//architectury.common(stonecutter.current.version)
architectury.common(stonecutter.tree.branches.mapNotNull {
if (stonecutter.current.project !in it) null
else it.prop("loom.platform")
// println(stonecutter.current.project !in it)
if (stonecutter.current.project !in it) return null
else return it.prop("loom.platform")
})
repositories {
@@ -118,8 +125,8 @@ dependencies {
// }
// })
if (minecraft=="1.21.9" || minecraft=="1.21.11") modApi("dev.architectury:architectury-neoforge:${archversion}")
else modApi("dev.architectury:architectury-forge:${archversion}") // NOTE: this could be wrong
if (minecraft=="1.21.1" || minecraft=="1.21.9" || minecraft=="1.21.11") modApi("dev.architectury:architectury-neoforge:${archversion}")
else modApi("dev.architectury:architectury-neoforge:${archversion}") // NOTE: this could be wrong
implementation("thedarkcolour:kotlinforforge-neoforge:6.0.0")
}

View File

@@ -27,7 +27,7 @@ stonecutter {
mc("forge", "1.20.1")
//WARNING: neoforge uses mods.toml instead of neoforge.mods.toml for versions 1.20.4 (?) and earlier
//mc("neoforge", "1.20.4", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11")
mc("neoforge", "1.20.4", "1.21.9", "1.21.11")
mc("neoforge", "1.20.4", "1.21.1", "1.21.9", "1.21.11")
}
create(rootProject)
}

View File

@@ -1,5 +1,5 @@
//? if fabric {
package org.neoflock.neocomputers.platforms.fabric;
/*package org.neoflock.neocomputers.platforms.fabric;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
@@ -10,4 +10,4 @@ public class ModMenuIntegration implements ModMenuApi {
return ConfigScreen::createConfigScreen;
}
}
//?}
*///?}

View File

@@ -1,5 +1,5 @@
//? if fabric {
package org.neoflock.neocomputers.platforms.fabric;
/*package org.neoflock.neocomputers.platforms.fabric;
import org.neoflock.neocomputers.ModPlatform;
import net.fabricmc.api.ModInitializer;
@@ -24,4 +24,4 @@ public class NeoComputersFabric implements ModInitializer {
}
}
}
//?}
*///?}

View File

@@ -0,0 +1,37 @@
package org.neoflock.neocomputers.platforms.fabric.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
import net.minecraft.client.color.item.ItemColor;
import net.minecraft.client.color.item.ItemColors;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.ItemLike;
import org.neoflock.neocomputers.NeoComputers;
import org.neoflock.neocomputers.block.Blocks;
import org.neoflock.neocomputers.block.CableBlock;
import org.neoflock.neocomputers.entity.BlockEntities;
import org.neoflock.neocomputers.entity.render.*;
import org.neoflock.neocomputers.item.Items;
import org.neoflock.neocomputers.platforms.fabric.client.model.ModelLoader;
public class NeoComputersFabricClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
ModelLoadingPlugin.register(new ModelLoader());
ColorProviderRegistry.BLOCK.register((state, world, pos, index) -> {
if (index == 0) {
return state.getValue(CableBlock.Companion.getCOLOR()).getTextureDiffuseColor();
} else {
return 0xFFFFFF;
}
}, Blocks.INSTANCE.getCABLE_BLOCK().get());
ColorProviderRegistry.ITEM.register((stack, index)-> {
return DyeColor.LIGHT_GRAY.getTextureDiffuseColor();
}, Items.INSTANCE.getITEMS().getRegistrar().get(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "cable")));
}
}

View File

@@ -0,0 +1,59 @@
package org.neoflock.neocomputers.platforms.fabric.client.model;
import kotlin.jvm.functions.Function1;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import org.neoflock.neocomputers.NeoComputers;
import org.neoflock.neocomputers.block.model.AbstractModel;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
public class FabricModelWrapper implements FabricBakedModel, UnbakedModel {
private AbstractModel model;
public FabricModelWrapper(AbstractModel model) {
this.model = model;
}
@Override
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<RandomSource> randomSupplier, RenderContext context) {
VanillaModelEncoder.emitBlockQuads(model, state, randomSupplier, context);
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<RandomSource> randomSupplier, RenderContext context) {
VanillaModelEncoder.emitItemQuads(model, null, randomSupplier, context);
}
@Override
public Collection<ResourceLocation> getDependencies() {
return List.of();
}
@Override
public void resolveParents(Function<ResourceLocation, UnbakedModel> resolver) {
}
@Override
public @Nullable BakedModel bake(ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state) {
// NeoComputers.INSTANCE.getLOGGER().info("{}", spriteGetter.apply(new Material()));
model._bake(spriteGetter);
// model.bake((Function1<? super ResourceLocation, ? extends TextureAtlasSprite>) Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS));
return model;
}
}

View File

@@ -0,0 +1,23 @@
package org.neoflock.neocomputers.platforms.fabric.client.model;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import org.neoflock.neocomputers.NeoComputers;
import org.neoflock.neocomputers.block.model.RobotModel;
public class ModelLoader implements ModelLoadingPlugin {
public static final ResourceLocation ROBOT = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "robot");
@Override
public void onInitializeModelLoader(Context pluginContext) {
pluginContext.modifyModelOnLoad().register((original, context) -> {
final ModelResourceLocation id = context.topLevelId();
if (id != null && id.id().equals(ROBOT)) {
return new FabricModelWrapper(new RobotModel());
}
return original;
});
}
}

View File

@@ -1,5 +1,5 @@
//? if neoforge {
/*package org.neoflock.neocomputers.platforms.neoforge;
package org.neoflock.neocomputers.platforms.neoforge;
import org.neoflock.neocomputers.ConfigScreen;
import org.neoflock.neocomputers.ModPlatform;
@@ -8,8 +8,8 @@ import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.common.Mod;
import org.neoflock.neocomputers.NeoComputers;
//? if <1.21 {
/^import net.neoforged.neoforge.client.ConfigScreenHandler;
^///?} else {
/*import net.neoforged.neoforge.client.ConfigScreenHandler;
*///?} else {
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
//?}
@Mod("neocomputers")
@@ -18,11 +18,11 @@ public class NeoComputersNeoForge {
NeoComputers.INSTANCE.entrypoint(new NeoForgePlatform());
ModLoadingContext.get().registerExtensionPoint(
//? if <1.21 {
/^ConfigScreenHandler.ConfigScreenFactory.class,
/*ConfigScreenHandler.ConfigScreenFactory.class,
() -> new ConfigScreenHandler.ConfigScreenFactory(
((client, parent) -> ConfigScreen.createConfigScreen(parent))
)
^///?} else {
*///?} else {
IConfigScreenFactory.class,
() -> (client, parent) -> ConfigScreen.createConfigScreen(parent)
//?}
@@ -40,4 +40,4 @@ public class NeoComputersNeoForge {
}
}
}
*///?}
//?}

View File

@@ -1,9 +1,11 @@
package org.neoflock.neocomputers
import dev.architectury.event.events.client.ClientLifecycleEvent
import dev.architectury.event.events.common.LifecycleEvent
import dev.architectury.event.events.common.PlayerEvent
import dev.architectury.event.events.common.TickEvent
import dev.architectury.networking.NetworkManager
import dev.architectury.platform.Platform
import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.block.Blocks
import org.neoflock.neocomputers.entity.BlockEntities
@@ -11,14 +13,17 @@ import org.neoflock.neocomputers.gui.menu.Menus
import dev.architectury.utils.Env
import dev.architectury.utils.EnvExecutor
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers
import net.minecraft.server.level.ServerPlayer
import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.block.NodeSynchronizer
import org.neoflock.neocomputers.block.DeviceBlockEntity
import org.neoflock.neocomputers.entity.render.EntityRenderers
import org.neoflock.neocomputers.gui.render.ScreenRenderer
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.item.Items
import org.neoflock.neocomputers.item.Tabs
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.NodeSynchronizer
import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.utils.FontProvider
import org.neoflock.neocomputers.utils.GenericContainerScreen
@@ -30,6 +35,12 @@ object NeoComputers {
val LOGGER: Logger = LoggerFactory.getLogger("NeoComputers")
var PLATFORM: ModPlatform? = null
// val BlockEntityRenderType: RenderType = RenderType.create(
// "nc_blockentities",
// DefaultVertexFormat.POSITION_TEX,
// VertexFormat.Mode.QUADS,
// 0xc000, RenderType.CompositeState.builder().setShaderState(RenderStateShard.POSITION_TEX_SHADER).createCompositeState(false)) // TODO: figure out correct buffer size and composite state
fun entrypoint(platform: ModPlatform?) {
PLATFORM = platform
@@ -43,14 +54,17 @@ object NeoComputers {
Tabs.TABS.register()
Sounds.SOUNDS.register()
ComponentRoles.mapDefaultTextures()
// i dont know why architectury wants two lambdas but whatever
// I don't know why architectury wants two lambdas but whatever
EnvExecutor.runInEnv(Env.CLIENT) {{
ClientLifecycleEvent.CLIENT_SETUP.register {
Menus.registerScreens()
Networking.allNodes.remove()
Networking.wirelessNodes.remove()
Networking.channels.remove()
EntityRenderers.registerBlockEntityRenderers()
}
ClientLifecycleEvent.CLIENT_STARTED.register {
FontProvider.load(ResourceLocation.fromNamespaceAndPath(MODID, "font/unscii.hex"))
ScreenRenderer.genUnboundTex();
}
ClientLifecycleEvent.CLIENT_STOPPING.register {
@@ -63,6 +77,16 @@ object NeoComputers {
NodeSynchronizer.syncScreens()
}
TickEvent.PLAYER_POST.register {
Sounds.tickCustomSounds()
}
LifecycleEvent.SERVER_STARTING.register {
Networking.allNodes.remove()
Networking.wirelessNodes.remove()
Networking.channels.remove()
}
PlayerEvent.CLOSE_MENU.register {
player, menu ->
if(player is ServerPlayer) NodeSynchronizer.playerScreenClosed(player)
@@ -73,22 +97,43 @@ object NeoComputers {
NodeSynchronizer.playerScreenClosed(player)
}
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, {
packet, ctx ->
val player = ctx.player
if(player is ServerPlayer) {
NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer)
}
})
// networking has no way to define a C2S packet type, so we need the listener on both
// however, defining it separately on both breaks both ends
// so we define it once, but on both platforms
if(Platform.getEnvironment() == Env.CLIENT || Platform.getEnvironment() == Env.SERVER) {
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, {
packet, ctx ->
val player = ctx.player
if(player is ServerPlayer) {
val ent = NodeSynchronizer.screenMap[player]
if(ent is DeviceNode) {
ent.processScreenInteraction(player, packet.buffer)
}
}
})
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.DeviceBlockStateRequest.TYPE, NodeSynchronizer.DeviceBlockStateRequest.CODEC, {
packet, ctx ->
val player = ctx.player
val level = player.level()
val dist = packet.blockPos.center.distanceTo(player.position())
if(player is ServerPlayer && dist <= NodeSynchronizer.MAX_STATE_DISTANCE_ALLOWED) {
val ent = level.getBlockEntity(packet.blockPos)
if(ent is DeviceBlockEntity) {
ent.sendStateToPlayer(player)
}
}
})
}
// we have to do this because the datagen task runs in the physical server
EnvExecutor.runInEnv(Env.CLIENT) {{
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC, {
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC, {
packet, ctx ->
val level = ctx.player.level()
val ent = level.getBlockEntity(packet.blockPos)
if(ent is NodeBlockEntity) {
ent.syncWithUpstream(packet.buffer)
if(ent is DeviceBlockEntity) {
ent.processCommits(packet.buffers)
}
})
@@ -99,11 +144,18 @@ object NeoComputers {
scr.processScreenStatePacket(packet.buffer)
}
})
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC, {
packet, ctx ->
// TODO: implement volume
Sounds.beep(packet.pos.center, packet.pattern, packet.freq, packet.duration.toMillis().toInt())
})
}}
EnvExecutor.runInEnv(Env.SERVER) {{
// https://github.com/architectury/architectury-api/issues/518
NetworkManager.registerS2CPayloadType(NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC)
NetworkManager.registerS2CPayloadType(NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC)
NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC)
NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC)
}}
LOGGER.info("Registered!")

View File

@@ -0,0 +1,16 @@
package org.neoflock.neocomputers.block
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.entity.AssemblerEntity
class AssemblerBlock : BaseBlock(), EntityBlock { // TODO: component stuff
override fun newBlockEntity(
pos: BlockPos,
state: BlockState
): BlockEntity? {
return AssemblerEntity(pos, state)
}
}

View File

@@ -5,6 +5,7 @@ import dev.architectury.registry.registries.DeferredRegister
import dev.architectury.registry.registries.Registrar
import dev.architectury.registry.registries.RegistrarManager
import dev.architectury.registry.registries.RegistrySupplier
import net.minecraft.client.renderer.RenderType
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.resources.ResourceKey
@@ -33,6 +34,11 @@ object Blocks {
val COMBUSTGEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("combustgen") { CombustionGeneratorBlock() }
val CASE_BLOCK: RegistrySupplier<Block> = BaseBlock.register("case") { CaseBlock() }
val REDSTONEIO_BLOCK: RegistrySupplier<Block> = BaseBlock.register("redio") { RedstoneIOBlock() }
val CABLE_BLOCK: RegistrySupplier<Block> = BaseBlock.register("cable") { CableBlock() }
val RELAY_BLOCK: RegistrySupplier<Block> = BaseBlock.register("relay") { RelayBlock() }
val ROBOT_BLOCK: RegistrySupplier<Block> = BaseBlock.register("robot") { RobotBlock() }
val RACK_BLOCK: RegistrySupplier<Block> = BaseBlock.register("rack") { RackBlock() }
val ASSEMBLER_BLOCK: RegistrySupplier<Block> = BaseBlock.register("assembler") { AssemblerBlock() }
fun registerBlockItems() {
BLOCKS.forEach(Consumer { sup: RegistrySupplier<Block> ->

View File

@@ -0,0 +1,176 @@
package org.neoflock.neocomputers.block
import net.minecraft.client.renderer.blockentity.PistonHeadRenderer
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.InteractionHand
import net.minecraft.world.ItemInteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.DyeItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.BooleanOp
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.CableEntity
class CableBlock() : DeviceBlock(Properties.of()), EntityBlock {
companion object {
val NORTH = BooleanProperty.create("north")
val EAST = BooleanProperty.create("east")
val WEST = BooleanProperty.create("west")
val SOUTH = BooleanProperty.create("south")
val UP = BooleanProperty.create("up")
val DOWN = BooleanProperty.create("down")
val COLOR = EnumProperty<DyeColor>.create("color", DyeColor::class.java)
val MIN = 0.375
val MAX = 1-MIN
val shapeCache: Array<VoxelShape?> = arrayOfNulls(Direction.entries.size*Direction.entries.size*Direction.entries.size*Direction.entries.size*Direction.entries.size*Direction.entries.size)
fun calcIdx(north: Boolean, south: Boolean, east: Boolean, west: Boolean, up: Boolean, down: Boolean): Int {
var idx = if (down) 1 else 0
idx += 2*(if (up) 1 else 0)
idx += 4*(if (west) 1 else 0)
idx += 8*(if (east) 1 else 0)
idx += 16*(if (south) 1 else 0)
idx += 32*(if (north) 1 else 0)
return idx
}
fun makeShapes() {
NeoComputers.LOGGER.info("[CABLE] recomputing shapes")
for (north in arrayOf(false, true)) { // shut up
for (south in arrayOf(false, true)) {
for (east in arrayOf(false, true)) {
for (west in arrayOf(false, true)) {
for (up in arrayOf(false, true)) {
for (down in arrayOf(false, true)) {
val shape = makeShape(north, south, east, west, up, down)
val idx = calcIdx(north, south, east,west, up, down)
shapeCache[idx] = shape;
}
}
}
}
}
}
}
fun makeShape(north: Boolean, south: Boolean, east: Boolean, west: Boolean, up: Boolean, down: Boolean): VoxelShape {
var shape = Shapes.box(MIN, MIN, MIN, MAX, MAX, MAX)
if (north) shape = Shapes.join(shape, Shapes.box(MIN, MIN, 0.0, MAX, MAX, MIN ), BooleanOp.OR)
if (south) shape = Shapes.join(shape, Shapes.box(MIN, MIN, MAX, MAX, MAX, 1.0 ), BooleanOp.OR)
if (east) shape = Shapes.join(shape, Shapes.box(MAX, MIN, MIN, 1.0, MAX, MAX), BooleanOp.OR)
if (west) shape = Shapes.join(shape, Shapes.box(0.0, MIN, MIN, MIN, MAX, MAX ), BooleanOp.OR)
if (up) shape = Shapes.join(shape, Shapes.box(MIN, MAX, MIN, MAX, 1.0, MAX), BooleanOp.OR)
if (down) shape = Shapes.join(shape, Shapes.box(MIN, 0.0, MIN, MAX, MIN, MAX ), BooleanOp.OR)
return shape
}
fun getPropByDirection(direction: Direction): BooleanProperty {
return when (direction) {
Direction.NORTH -> NORTH
Direction.SOUTH -> SOUTH
Direction.WEST -> WEST
Direction.EAST -> EAST
Direction.UP -> UP
Direction.DOWN -> DOWN
}
}
fun shouldConnect(pos: BlockPos, npos: BlockPos, level: Level): Boolean {
val ent = level.getBlockEntity(npos)
val blockState = level.getBlockState(pos)
val theirState = level.getBlockState(npos)
val universal = DyeColor.LIGHT_GRAY
if(ent is CableEntity) {
val ourColor = blockState.getValue(COLOR)
val theirColor = theirState.getValue(COLOR)
if(ourColor.equals(universal)) return true
if(theirColor.equals(universal)) return true
if(ourColor.equals(theirColor)) return true
return false
}
return ent is DeviceBlockEntity
}
}
init {
registerDefaultState(stateDefinition.any()
.setValue(NORTH, false)
.setValue(EAST, false)
.setValue(WEST, false)
.setValue(SOUTH, false)
.setValue(UP, false)
.setValue(DOWN, false)
.setValue(COLOR, DyeColor.LIGHT_GRAY)
)
makeShapes()
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
super.createBlockStateDefinition(builder
.add(NORTH)
.add(EAST)
.add(SOUTH)
.add(WEST)
.add(UP)
.add(DOWN)
.add(COLOR))
}
override fun newBlockEntity(pos: BlockPos, state: BlockState) = CableEntity(pos, state)
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? {
val idx = calcIdx(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN))
return shapeCache[idx];
// return makeShape(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN))
}
override fun neighborChanged(state: BlockState, level: Level, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, movedByPiston: Boolean) {
// val neighbors = getNeighbourEntities(blockPos, level)
// for (dir in Direction.entries) {
// val ent = level.getBlockEntity(blockPos.relative(dir))
// level.setBlockAndUpdate(blockPos, blockState.setValue(getPropByDirection(dir), (ent is NodeBlockEntity || ent is CableEntity)))
// }
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston)
val diff = pos.subtract(neighborPos)
val dir = Direction.fromDelta(diff.x, diff.y, diff.z)!!.opposite
val ent = level.getBlockEntity(neighborPos)
// val value = ent is NodeBlockEntity || (ent is CableEntity && (level.getBlockState(neighborPos).getValue(COLOR).equals(state.getValue(COLOR)) || state.getValue(COLOR).equals(DyeColor.LIGHT_GRAY)))
level.setBlockAndUpdate(pos, state.setValue(getPropByDirection(dir), shouldConnect(pos, neighborPos, level)))
}
override fun useItemOn(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hand: InteractionHand, hitResult: BlockHitResult): ItemInteractionResult? {
// return super.useItemOn(stack, state, level, pos, player, hand, hitResult)
if (stack.item is DyeItem) {
val dyeitem = stack.item as DyeItem
level.setBlockAndUpdate(pos, state.setValue(COLOR, dyeitem.dyeColor))
val ent = level.getBlockEntity(pos)
if(ent is CableEntity) {
ent.connectionsAreDirty = true
}
return ItemInteractionResult.SUCCESS
}
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
}
}

View File

@@ -1,41 +1,53 @@
package org.neoflock.neocomputers.block
import net.minecraft.client.player.LocalPlayer
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.ChatType
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.OutgoingChatMessage
import net.minecraft.network.chat.PlayerChatMessage
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.PowerRole
import kotlin.math.min
open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : NodeBlockEntity(type, pos, state) {
open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : DeviceBlockEntity(type, pos, state) {
override val node = object : Networking.Node() {
val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.STORAGE
override var energyCapacity: Long = capacity
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
super.writeFullStateCommit(buf)
buf.writeVarLong(energy)
}
override fun processCommit(buf: FriendlyByteBuf) {
super.processCommit(buf)
energy = buf.readVarLong()
}
}
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
node.energy = min(compoundTag.getLong("energy"), node.energyCapacity)
// TODO: cache list
override fun getDeviceNodes() = listOf(deviceNode)
override fun getNodeFromSide(directionToRequester: Direction) = deviceNode
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
deviceNode.energy = min(tag.getLong("energy"), deviceNode.energyCapacity)
}
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy)
compoundTag.putLong("energy", deviceNode.energy)
}
}
@@ -43,7 +55,7 @@ class CapacitorEntityTier1(pos: BlockPos, state: BlockState): CapacitorEntity(20
class CapacitorEntityTier2(pos: BlockPos, state: BlockState): CapacitorEntity(50000, BlockEntities.CAPACITOR2_ENTITY.get(), pos, state)
class CapacitorEntityTier3(pos: BlockPos, state: BlockState): CapacitorEntity(100000, BlockEntities.CAPACITOR3_ENTITY.get(), pos, state)
class CapacitorBlock(val tier: Int) : NodeBlock() {
class CapacitorBlock(val tier: Int) : DeviceBlock() {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
val cap: CapacitorEntity = when(tier) {
1 -> CapacitorEntityTier1(blockPos, blockState)
@@ -51,7 +63,7 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
3 -> CapacitorEntityTier3(blockPos, blockState)
else -> throw UnsupportedOperationException("unsupported tier: $tier")
}
return cap.initNetworking()
return cap
}
override fun useWithoutItem(
@@ -62,12 +74,11 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
blockHitResult: BlockHitResult
): InteractionResult {
if(level.isClientSide()) {
val p = player as LocalPlayer
val ent = level.getBlockEntity(blockPos)
if(ent is CapacitorEntity) {
if(p.isCrouching) ent.node.giveEnergy(1)
val msg = PlayerChatMessage.system("energy: ${ent.node.energy} / ${ent.capacity} (${ent.computeEdges().size} edges, ${ent.node.getReachable().size} connected)")
p.sendSystemMessage(OutgoingChatMessage.create(msg).content())
if(player.isCrouching) ent.deviceNode.giveEnergy(1)
val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.deviceNode.connections.size} connections, ${ent.deviceNode.getReachable().size} reachable)")
player.sendSystemMessage(OutgoingChatMessage.create(msg).content())
}
}
return InteractionResult.SUCCESS

View File

@@ -3,33 +3,27 @@ package org.neoflock.neocomputers.block;
import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.util.RandomSource
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.CombustionGeneratorBlock.Companion.COMBUSTGEN_ACTIVE
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.CaseBlockEntity
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.network.NodeSynchronizer
class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance)) { // placeholder stuff
class CaseBlock() : DeviceBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance).noOcclusion()) { // placeholder stuff
companion object {
val FACING: EnumProperty<Direction> = EnumProperty.create<Direction>("facing", Direction::class.java)
val COMPUTER_RUNNING = BooleanProperty.create("running")!!
fun getLuminance(blockState: BlockState): Int {
@@ -37,27 +31,34 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
}
}
init {
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(COMPUTER_RUNNING, false))
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState)
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
builder.add(COMPUTER_RUNNING)
builder.add(FACING)
}
fun getMachine(level: BlockGetter, blockPos: BlockPos): CaseBlockEntity {
return level.getBlockEntity(blockPos) as CaseBlockEntity
}
override fun isSignalSource(state: BlockState): Boolean = true
override fun getSignal(
blockState: BlockState,
blockGetter: BlockGetter,
blockPos: BlockPos,
direction: Direction
): Int {
return getMachine(blockGetter, blockPos).redstoneOut[dirToIdx(direction.opposite)]
return getMachine(blockGetter, blockPos).redstoneOut[direction.opposite.ordinal]
}
override fun onPlace(
blockState: BlockState,
state: BlockState,
level: Level,
blockPos: BlockPos,
blockState2: BlockState,
@@ -67,7 +68,11 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
level.updateNeighborsAt(blockPos, this)
getMachine(level, blockPos).refetchAllRedstone()
}
super.onPlace(blockState, level, blockPos, blockState2, bl)
super.onPlace(state, level, blockPos, blockState2, bl)
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
return defaultBlockState().setValue(FACING, context.horizontalDirection.opposite)
}
override fun neighborChanged(
@@ -94,8 +99,14 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
): InteractionResult {
if(!level.isClientSide) {
val ent = level.getBlockEntity(blockPos, BlockEntities.CASE_ENTITY.get()).get()
MenuRegistry.openMenu(player as ServerPlayer, ent)
NodeSynchronizer.registerPlayerScreen(player, ent)
if(player.isCrouching) {
// Quickstat
ent.start()
} else {
// Open menu
MenuRegistry.openMenu(player as ServerPlayer, ent)
NodeSynchronizer.registerPlayerScreen(player, ent.deviceNode)
}
}
return InteractionResult.SUCCESS
}

View File

@@ -0,0 +1,254 @@
package org.neoflock.neocomputers.block
import dev.architectury.networking.NetworkManager
import io.netty.buffer.Unpooled
import net.minecraft.client.Minecraft
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.NodeSynchronizer
abstract class SingleDeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): DeviceBlockEntity(type, pos, state) {
abstract val deviceNode: DeviceNode
override fun getDeviceNodes() = listOf(deviceNode)
override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? = deviceNode
}
abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): BlockEntity(type, pos, state) {
val connetionsInDir = MutableList<DeviceNode?>(Direction.entries.size) { null }
val nodesInDir = MutableList<DeviceNode?>(Direction.entries.size) { null }
var alreadySetup = false
var receivedServerState = false
var connectionsAreDirty = true
abstract fun getDeviceNodes(): List<DeviceNode>
// Gets, if applicable, the node from a direction.
// The direction is from this block entity to the original requester,
// so it is Direction.UP if we asked from the one on the top side.
abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode?
open fun processCommits(commits: Iterable<FriendlyByteBuf>) {
receivedServerState = true
val devs = getDeviceNodes()
for (buf in commits) {
val idx = buf.readVarInt()
if(idx >= 0 && idx < devs.size) {
devs[idx].processCommit(buf)
}
}
}
open fun initNetworking(): DeviceBlockEntity {
if(hasLevel()) {
alreadySetup = true
Networking.addNodes(getDeviceNodes())
Direction.entries.forEach { handleConnectionsFor(it) }
}
return this
}
// Cables are 1 node
open fun getCurrentlyConnectedNodeIn(direction: Direction): DeviceNode? {
val ent = level?.getBlockEntity(blockPos.relative(direction))
if(ent is DeviceBlockEntity) {
return ent.getNodeFromSide(direction.opposite)
}
return null
}
open fun handleConnectionsFor(direction: Direction) {
// refuse connections on no node to reduce CPU load
val node = getNodeFromSide(direction)
val oldNode = nodesInDir[direction.ordinal]
nodesInDir[direction.ordinal] = node
val old = connetionsInDir[direction.ordinal]
val now = getCurrentlyConnectedNodeIn(direction)
if(node == null) {
if(old != null) oldNode?.disconnectFrom(old)
if(now != null) oldNode?.disconnectFrom(now)
return
}
if(oldNode != null && oldNode.address != node.address) {
if(old != null) oldNode.disconnectFrom(old)
if(now != null) oldNode.disconnectFrom(now)
}
if(old?.address != now?.address || oldNode == null) {
if(old != null) node.disconnectFrom(old)
if(now != null) node.connectTo(now)
}
// bullshit hack
if(now != null) {
node.connectTo(now)
}
connetionsInDir[direction.ordinal] = now
}
// TODO: optimize this sometime before our test computers melt
open fun tickDevice(level: Level) {
// Handles device connections
// we do it like this because stinky MC will call stuff before world is fully setup
// and then not notify us of neighbour changes
// this is because MC is considered shit
if(!alreadySetup) {
initNetworking()
}
if(connectionsAreDirty) {
connectionsAreDirty = false
Direction.entries.forEach { handleConnectionsFor(it) }
}
}
open fun sendCommitsToClient(level: Level) {
if(level !is ServerLevel) return
// synchronization!
val commits = mutableListOf<FriendlyByteBuf>()
val devs = getDeviceNodes()
for((i, dev) in devs.withIndex()) {
if(dev.outOfSync) {
dev.outOfSync = false
val buf = FriendlyByteBuf(Unpooled.buffer())
buf.writeVarInt(i)
dev.writeFullStateCommit(buf)
commits.addLast(buf)
}
}
if(commits.isNotEmpty()) {
level.players().forEach {
val dist = it.position().distanceTo(blockPos.center)
if(dist < 100) NetworkManager.sendToPlayer(it, NodeSynchronizer.DeviceBlockStatePayload(blockPos, commits))
}
}
}
open fun sendStateToPlayer(player: ServerPlayer) {
val world = level!!
if(world !is ServerLevel) return
// synchronization!
val commits = mutableListOf<FriendlyByteBuf>()
val devs = getDeviceNodes()
for((i, dev) in devs.withIndex()) {
val buf = FriendlyByteBuf(Unpooled.buffer())
buf.writeVarInt(i)
dev.writeFullStateCommit(buf)
commits.addLast(buf)
}
if(commits.isNotEmpty()) {
world.players().forEach {
val dist = it.position().distanceTo(blockPos.center)
if(dist <= NodeSynchronizer.MAX_STATE_DISTANCE_ALLOWED) NetworkManager.sendToPlayer(it, NodeSynchronizer.DeviceBlockStatePayload(blockPos, commits))
}
}
}
open fun requestServerState() {
// no point
if(receivedServerState) return
// we're the server bro :sob:
if(level?.isClientSide != true) return
val player = Minecraft.getInstance().player ?: return
// we assume the player will just reject, so we save on bandwidth
if(player.position().distanceTo(blockPos.center) > NodeSynchronizer.MAX_STATE_DISTANCE_ALLOWED) return
NetworkManager.sendToServer(NodeSynchronizer.DeviceBlockStateRequest(blockPos))
}
override fun setRemoved() {
super.setRemoved()
alreadySetup = false
Networking.removeNodes(getDeviceNodes())
}
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
for (node in getDeviceNodes()) {
node.markChanged()
}
connectionsAreDirty = true
receivedServerState = false
}
}
abstract class DeviceBlock(properties: Properties = Properties.of()): BaseBlock(properties), EntityBlock {
override fun <T : BlockEntity?> getTicker(
level: Level,
state: BlockState,
blockEntityType: BlockEntityType<T?>
): BlockEntityTicker<T> {
return object : BlockEntityTicker<T> {
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T & Any) {
if(blockEntity !is DeviceBlockEntity) return
blockEntity.tickDevice(level)
blockEntity.sendCommitsToClient(level)
if(level.isClientSide) {
blockEntity.requestServerState()
}
}
}
}
override fun onPlace(state: BlockState, level: Level, pos: BlockPos, oldState: BlockState, movedByPiston: Boolean) {
super.onPlace(state, level, pos, oldState, movedByPiston)
val ent = level.getBlockEntity(pos)
if(ent is DeviceBlockEntity) {
ent.initNetworking()
ent.connectionsAreDirty = true
}
}
override fun useWithoutItem(
state: BlockState,
level: Level,
pos: BlockPos,
player: Player,
hitResult: BlockHitResult
): InteractionResult? {
val ent = level.getBlockEntity(pos)
if(ent is DeviceBlockEntity && player is ServerPlayer) {
val dir = hitResult.direction
val node = ent.getNodeFromSide(dir)
if(node == null) {
player.sendSystemMessage(Component.literal("no node for dir $dir"))
} else {
player.sendSystemMessage(Component.literal("dir: $dir, address: ${node.address}, connections: ${node.connections.joinToString(", ")}"))
}
}
return super.useWithoutItem(state, level, pos, player, hitResult)
}
override fun neighborChanged(
state: BlockState,
level: Level,
pos: BlockPos,
neighborBlock: Block,
neighborPos: BlockPos,
movedByPiston: Boolean
) {
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston)
val ent = level.getBlockEntity(pos)
if(ent is DeviceBlockEntity) {
ent.handleConnectionsFor(Direction.getNearest(neighborPos.center.subtract(pos.center)))
}
}
}

View File

@@ -23,13 +23,14 @@ import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.SolarGeneratorBlockEntity
import org.neoflock.neocomputers.entity.CombustionGeneratorBlockEntity
import org.neoflock.neocomputers.network.NodeSynchronizer
class SolarGeneratorBlock : NodeBlock(), EntityBlock {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState).initNetworking()
class SolarGeneratorBlock : DeviceBlock(), EntityBlock {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState)
}
// TODO: make it glow when burning
class CombustionGeneratorBlock : NodeBlock, EntityBlock {
class CombustionGeneratorBlock : DeviceBlock, EntityBlock {
companion object {
val COMBUSTGEN_ACTIVE = BooleanProperty.create("active")
@@ -42,7 +43,7 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock {
registerDefaultState(defaultBlockState().setValue(COMBUSTGEN_ACTIVE, false))
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = CombustionGeneratorBlockEntity(blockPos, blockState).initNetworking()
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = CombustionGeneratorBlockEntity(blockPos, blockState)
override fun useWithoutItem(
blockState: BlockState,
@@ -54,7 +55,7 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock {
if(!level.isClientSide()) {
val sp = player as ServerPlayer
val ent = level.getBlockEntity(blockPos, BlockEntities.COMBUSTGEN_ENTITY.get()).get()
NodeSynchronizer.registerPlayerScreen(sp, ent)
NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
MenuRegistry.openMenu(sp, ent)
}
return InteractionResult.SUCCESS

View File

@@ -1,269 +0,0 @@
package org.neoflock.neocomputers.block
import dev.architectury.networking.NetworkManager
import io.netty.buffer.Unpooled
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.network.Networking
object NodeSynchronizer {
class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload {
companion object {
val NODE_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "node_sync")
val TYPE = CustomPacketPayload.Type<StatePayload>(NODE_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, StatePayload> {
override fun decode(buf: RegistryFriendlyByteBuf): StatePayload {
val blockPos = buf.readBlockPos()
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
return StatePayload(blockPos, buffer)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: StatePayload) {
buf.writeBlockPos(payload.blockPos)
buf.writeBytes(payload.buffer)
}
}
}
override fun type() = TYPE
}
class ScreenPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
companion object {
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")
val TYPE = CustomPacketPayload.Type<ScreenPayload>(SCREEN_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenPayload> {
override fun decode(buf: RegistryFriendlyByteBuf): ScreenPayload {
val id = buf.readByteArray().decodeToString()
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
return ScreenPayload(id, buffer)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenPayload) {
buf.writeByteArray(payload.entityTypeWireID.encodeToByteArray())
buf.writeBytes(payload.buffer)
}
}
}
override fun type() = TYPE
}
class ScreenDataPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
companion object {
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload {
val id = buf.readByteArray().decodeToString()
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
return ScreenDataPayload(id, buffer)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenDataPayload) {
buf.writeByteArray(payload.entityTypeWireID.encodeToByteArray())
buf.writeBytes(payload.buffer)
}
}
}
override fun type() = TYPE
}
val screenMap = mutableMapOf<ServerPlayer, NodeBlockEntity>()
fun playerScreenClosed(player: ServerPlayer) {
screenMap.remove(player)
}
fun nodeTypeToWireID(nodeType: BlockEntityType<*>): String = nodeType.javaClass.canonicalName
fun registerPlayerScreen(player: ServerPlayer, entity: NodeBlockEntity) {
screenMap[player] = entity
}
fun syncScreens() {
for((player, ent) in screenMap) {
val buf = FriendlyByteBuf(Unpooled.buffer())
ent.encodeScreenData(player, buf)
NetworkManager.sendToPlayer(player, ScreenPayload(nodeTypeToWireID(ent.type), buf))
}
}
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
NetworkManager.sendToServer(ScreenDataPayload("", friendlyByteBuf))
}
}
abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) {
abstract val node: Networking.Node
fun initNetworking(): NodeBlockEntity {
Networking.addNode(node)
invalidateNodeState()
return this
}
// runs on the server, meant to encode state to send to all players
open fun encodeDownstreamData(packet: FriendlyByteBuf) {
packet.writeUUID(node.address)
packet.writeLong(node.energy)
packet.writeLong(node.energyCapacity)
packet.writeEnum(node.reachability)
packet.writeEnum(node.powerRole)
}
// runs on the client, meant to decode server state packets to synchronize client state
open fun syncWithUpstream(packet: FriendlyByteBuf) {
node.address = packet.readUUID()
node.energy = packet.readLong()
node.energyCapacity = packet.readLong()
node.reachability = packet.readEnum(node.reachability.javaClass)
node.powerRole = packet.readEnum(node.powerRole.javaClass)
}
// Encodes data meant for the associated screen of a player
open fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {}
open fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) {}
private var stateIsDirty = true
open fun getNeighbourEntities(): List<BlockEntity> {
val subpos = listOf(
blockPos.offset(0, 0, 1),
blockPos.offset(0, 0, -1),
blockPos.offset(0, 1, 0),
blockPos.offset(0, -1, 0),
blockPos.offset(1, 0, 0),
blockPos.offset(-1, 0, 0),
)
return subpos.mapNotNull { pos -> level?.getBlockEntity(pos) }
}
open fun computeEdges(): Set<NodeBlockEntity> {
val s = mutableSetOf<NodeBlockEntity>()
val neighbours = getNeighbourEntities()
for(neighbour in neighbours) {
if(neighbour is NodeBlockEntity) s.add(neighbour);
// TODO: handle cable entities
}
s.remove(this)
return s
}
open fun invalidateNodeState() {
stateIsDirty = true
}
fun needsSynchronization() = stateIsDirty
open fun tickNode(level: Level) {
if(!level.isClientSide) {
val l = level as ServerLevel
val packetBuf = FriendlyByteBuf(Unpooled.buffer())
encodeDownstreamData(packetBuf)
l.players().forEach {
if(it.level().isLoaded(blockPos)) NetworkManager.sendToPlayer(it, NodeSynchronizer.StatePayload(blockPos, packetBuf))
}
}
if(!stateIsDirty) return
stateIsDirty = false
computeEdges().forEach {
node.connectTo(it.node)
}
}
override fun setChanged() {
invalidateNodeState()
computeEdges().forEach { it.invalidateNodeState() }
super.setChanged()
}
override fun setRemoved() {
super.setRemoved()
Networking.removeNode(node)
}
override fun clearRemoved() {
super.clearRemoved()
initNetworking()
}
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
invalidateNodeState()
computeEdges().forEach { it.invalidateNodeState() }
}
}
abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(properties), EntityBlock {
override fun <T : BlockEntity> getTicker(
level: Level,
blockState: BlockState,
blockEntityType: BlockEntityType<T>
): BlockEntityTicker<T>? {
return object : BlockEntityTicker<T> {
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T) {
if(blockEntity !is NodeBlockEntity) return;
blockEntity.tickNode(level)
}
}
}
override fun onPlace(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
blockState2: BlockState,
bl: Boolean
) {
super.onPlace(blockState, level, blockPos, blockState2, bl)
if(!level.isClientSide) {
val ent = level.getBlockEntity(blockPos)
if(ent is NodeBlockEntity) {
ent.invalidateNodeState()
ent.computeEdges().forEach { it.invalidateNodeState() }
}
level.updateNeighborsAt(blockPos, this)
}
}
override fun neighborChanged(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
block: Block,
blockPos2: BlockPos,
bl: Boolean
) {
super.neighborChanged(blockState, level, blockPos, block, blockPos2, bl)
if(!level.isClientSide) {
val ent = level.getBlockEntity(blockPos)
if(ent is NodeBlockEntity) {
ent.invalidateNodeState()
}
}
}
}

View File

@@ -0,0 +1,69 @@
package org.neoflock.neocomputers.block
import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.RackEntity
import org.neoflock.neocomputers.network.NodeSynchronizer
class RackBlock : DeviceBlock(Properties.of().noOcclusion()), EntityBlock {
override fun newBlockEntity(
pos: BlockPos,
state: BlockState
): BlockEntity? {
return RackEntity(pos, state)
}
// override fun getShape(
// state: BlockState,
// level: BlockGetter,
// pos: BlockPos,
// context: CollisionContext
// ): VoxelShape? {
// return Shapes.box(0.0,0.0,0.0,0.01,0.01,0.01)
// }
// override fun getRenderShape(state: BlockState): RenderShape? {
// return RenderShape
// }
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hitResult: BlockHitResult): InteractionResult? {
val res = hitResult.location
val ent = level.getBlockEntity(pos, BlockEntities.RACK_ENTITY.get()).get()
if(res.x == 18.0) { // TODO: handle rotation
NeoComputers.LOGGER.info("{} > {} > {}, {} > {} > {}", pos.z+15/16f, res.z, pos.z+1/16f, pos.y+14/16f, res.y, pos.y+2/16f)
if (pos.z + 15 / 16f > res.z && res.z > pos.z + 1 / 16f && pos.y + 14 / 16f > res.y && res.y > pos.y + 2 / 16f) {
var rack = 0
rack += if(res.y < pos.y+5/16f) 1 else 0
rack += if(res.y < pos.y+8/16f) 1 else 0
rack += if(res.y < pos.y+12/16f) 1 else 0
player.sendSystemMessage(Component.literal(String.format("Hit server #%d", rack))) // TODO: call some RackItem method
return InteractionResult.SUCCESS
}
}
if (!level.isClientSide) {
MenuRegistry.openExtendedMenu(player as ServerPlayer, ent)
NodeSynchronizer.registerPlayerScreen(player as ServerPlayer, ent.node)
}
return InteractionResult.SUCCESS
}
}

View File

@@ -7,30 +7,27 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.RedStoneWireBlock
import net.minecraft.world.level.block.RedstoneTorchBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.DeviceNode
fun dirToIdx(direction: Direction) = Direction.entries.indexOf(direction)
class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEntity(BlockEntities.REDSTONEIO_ENTITY.get(), blockPos, blockState) {
class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.REDSTONEIO_ENTITY.get(), blockPos, blockState) {
val redstoneIn = Array<Int>(Direction.entries.size) {0}
val redstoneOut = Array<Int>(Direction.entries.size) {0}
// TODO: have redstone I/O node for component and shi
override val node = object : Networking.Node() {
override val deviceNode = object : DeviceNode() {
}
fun refetch(dir: Direction) {
val src = blockPos.offset(dir.stepX, dir.stepY, dir.stepZ)
val cur = level?.getSignal(src, dir) ?: 0
val idx = dirToIdx(dir)
val idx = dir.ordinal
if(redstoneIn[idx] != cur) {
onRedstoneSignalChanged(dir, redstoneIn[idx], cur)
}
@@ -42,12 +39,12 @@ class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnt
}
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
Networking.emitMessage(node, Networking.ComputerUncheckedSignal(node, "redstone_changed", arrayOf(node.address.toString(), dirToIdx(dir), oldValue, newValue)))
Networking.emitMessage(deviceNode, Networking.ComputerUncheckedSignal(deviceNode, "redstone_changed", arrayOf(deviceNode.address.toString(), dir.ordinal, oldValue, newValue)))
NeoComputers.LOGGER.info("redstone in direction ${dir.name} changed from $oldValue to $newValue")
}
}
class RedstoneIOBlock(): NodeBlock(Properties.of().isRedstoneConductor { state, getter, pos -> true }) {
class RedstoneIOBlock(): DeviceBlock(Properties.of().isRedstoneConductor { state, getter, pos -> true }) {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = RedstoneIOEntity(blockPos, blockState)
fun getRedstoneIO(level: BlockGetter, blockPos: BlockPos): RedstoneIOEntity? {
@@ -68,7 +65,7 @@ class RedstoneIOBlock(): NodeBlock(Properties.of().isRedstoneConductor { state,
): Int {
val redstoneIO = getRedstoneIO(blockGetter, blockPos)
if(redstoneIO != null) {
return redstoneIO.redstoneOut[dirToIdx(direction.opposite)]
return redstoneIO.redstoneOut[direction.opposite.ordinal]
}
return super.getSignal(blockState, blockGetter, blockPos, direction)
}
@@ -126,7 +123,7 @@ class RedstoneIOBlock(): NodeBlock(Properties.of().isRedstoneConductor { state,
val redio = getRedstoneIO(level, blockPos)
val dir = blockHitResult.direction
if (redio != null) {
val idx = dirToIdx(dir)
val idx = dir.ordinal
redio.redstoneOut[idx]++
redio.redstoneOut[idx] %= 16
NeoComputers.LOGGER.info("outputting redstone level ${redio.redstoneOut[idx]} on ${dir.name}")

View File

@@ -0,0 +1,194 @@
package org.neoflock.neocomputers.block
import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.ContainerHelper
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.menu.RelayMenu
import org.neoflock.neocomputers.item.RelayUpgrade
import org.neoflock.neocomputers.network.ConventionalNetworkDevice
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.NNComponent
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.NodeSynchronizer
import org.neoflock.neocomputers.utils.GenericContainer
class RelayEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.RELAY_ENTITY.get(), blockPos, blockState),
GenericContainer, ComponentUser, MenuProvider {
companion object RelaySlots {
val CARD = 0
val CPU = 1
val MEM = 2
val STORAGE = 3
val SLOT_COUNT = 4
}
val slots = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY)!!
override fun getItems(): NonNullList<ItemStack> = slots
override fun stillValid(player: Player): Boolean = true
fun getUpgrade(slot: Int) = slots[slot].item as? RelayUpgrade
fun computeRelayInterval(): Int = getUpgrade(CPU)?.getRelayInterval(slots[CPU]) ?: 5
fun computeRelayBufferSize(): Int = getUpgrade(MEM)?.getRelayBufferSize(slots[MEM]) ?: 1
fun computeRelayQueueSize(): Int = getUpgrade(STORAGE)?.getRelayQueueSize(slots[STORAGE]) ?: 20
fun getRelaySender(): DeviceNode? = getUpgrade(CARD)?.getComponentNode(slots[CARD])
fun computeRelayCapacity(): Int = computeRelayBufferSize() + computeRelayQueueSize()
val queue = mutableListOf<Networking.ClassicPacket>()
val justReceived = mutableListOf<Networking.ClassicPacket>()
var activityTickLeft = 0
var ticksUntilQueue = 0
override val deviceNode = object : DeviceNode() {
override var reachability = Networking.Visibility.NONE
override fun received(message: Networking.Message) {
super.received(message)
if(message.sender == this) return
if(message is Networking.ClassicPacket && message.hopCount < 5 && (queue.size + justReceived.size) < computeRelayCapacity()) {
justReceived.addLast(message)
activityTickLeft = 20
markChanged()
}
}
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
super.encodeScreenData(player, buf)
buf.writeVarInt(computeRelayInterval())
buf.writeVarInt(computeRelayBufferSize())
buf.writeVarInt(computeRelayQueueSize())
buf.writeVarInt(queue.size + justReceived.size)
}
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
super.writeFullStateCommit(buf)
buf.writeVarInt(activityTickLeft)
}
override fun processCommit(buf: FriendlyByteBuf) {
super.processCommit(buf)
activityTickLeft = buf.readVarInt()
}
override fun getComponent() = NNComponent("relay")
}
fun sendQueuedPacket() {
if(queue.isEmpty()) return
val pack = queue.removeFirst()
for(connection in deviceNode.connections) {
// skip unwanted loopback
if(connection in pack.sender.getReachable()) continue
val hopped = pack.hop(pack.sender)
if(connection is ConventionalNetworkDevice) {
connection.sendClassicPacket(hopped)
} else {
Networking.emitMessage(connection, hopped, setOf(deviceNode))
}
}
}
override fun tickDevice(level: Level) {
super.tickDevice(level)
if(level !is ServerLevel) return
if(activityTickLeft > 0) {
activityTickLeft--
deviceNode.markChanged()
}
queue.addAll(justReceived)
justReceived.clear()
val cap = computeRelayCapacity()
while(queue.size > cap) queue.removeLast()
ticksUntilQueue--
if(ticksUntilQueue <= 0) {
ticksUntilQueue = computeRelayInterval()
val toSend = computeRelayBufferSize()
for(i in 0..<toSend) {
sendQueuedPacket()
}
}
deviceNode.markChanged()
}
override fun getMachineBlockPosition() = blockPos!!
override fun getMachineLevel() = level!!
override fun getMachineNode() = deviceNode
override fun getDisplayName() = Component.translatable("block.neocomputers.relay")!!
override fun createMenu(
i: Int,
inventory: Inventory,
player: Player
) = RelayMenu(i, inventory, this)
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
ContainerHelper.loadAllItems(tag, slots, registries)
}
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
ContainerHelper.saveAllItems(tag, slots, registries)
}
}
class RelayBlock: DeviceBlock(Properties.of().sound(SoundType.METAL)) {
override fun newBlockEntity(pos: BlockPos, state: BlockState) = RelayEntity(pos, state)
override fun useWithoutItem(
state: BlockState,
level: Level,
pos: BlockPos,
player: Player,
hitResult: BlockHitResult
): InteractionResult {
if(!level.isClientSide) {
val ent = level.getBlockEntity(pos, BlockEntities.RELAY_ENTITY.get()).get()
// Open menu
MenuRegistry.openMenu(player as ServerPlayer, ent)
NodeSynchronizer.registerPlayerScreen(player, ent.deviceNode)
}
return InteractionResult.SUCCESS
}
override fun onRemove(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
blockState2: BlockState,
bl: Boolean
) {
Containers.dropContentsOnDestroy(blockState, blockState2, level, blockPos)
super.onRemove(blockState, level, blockPos, blockState2, bl)
}
}

View File

@@ -0,0 +1,33 @@
package org.neoflock.neocomputers.block
import net.minecraft.core.BlockPos
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockBehaviour
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.RobotEntity
class RobotBlock : BaseBlock(Properties.of().noOcclusion()), EntityBlock { // todo: node stuff
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
NeoComputers.LOGGER.info("block entity created..")
return RobotEntity(pos, state)
}
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? {
return Shapes.box(0.1, 0.1, 0.1, 0.9, 0.9, 0.9)
}
override fun getRenderShape(state: BlockState): RenderShape {
return RenderShape.INVISIBLE // this is so not good
}
// public RenderShape getRenderShape(BlockState state) {
// return RenderShape.ENTITYBLOCK_ANIMATED;
// }
}

View File

@@ -1,36 +1,45 @@
package org.neoflock.neocomputers.block;
import dev.architectury.registry.menu.ExtendedMenuProvider
import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.EnumProperty
import net.minecraft.world.level.block.state.properties.EnumProperty.*
import net.minecraft.world.level.block.state.properties.IntegerProperty
import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.gui.menu.Menus
import org.neoflock.neocomputers.gui.menu.ScreenMenu
import org.neoflock.neocomputers.network.Networking
import kotlin.math.abs
import kotlin.math.max
import org.neoflock.neocomputers.network.NodeSynchronizer
class ScreenBlock() : NodeBlock() {
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? {
val scr = ScreenEntity(blockPos, blockState)
scr.initNetworking()
return scr
class ScreenBlock() : DeviceBlock() {
companion object {
val FACING_HORIZ: EnumProperty<Direction> = EnumProperty.create<Direction>("facing_horiz", Direction::class.java)
val FACING_VERTI: IntegerProperty = IntegerProperty.create("facing_verti", 0, 2) // "Integer" property doesnt accept values below 0, also death to enums, long live magic numbers
}
init {
registerDefaultState(stateDefinition.any().setValue(FACING_HORIZ, Direction.NORTH).setValue(FACING_VERTI, 1))
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = ScreenEntity(blockPos, blockState)
override fun useWithoutItem(
blockState: BlockState,
level: Level,
@@ -39,18 +48,45 @@ class ScreenBlock() : NodeBlock() {
blockHitResult: BlockHitResult
): InteractionResult {
if(!level.isClientSide) {
val screenState = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
if(!screenState.node.consumeEnergy(5)) {
player.sendSystemMessage(Component.literal("Not enough power."))
return InteractionResult.SUCCESS
};
MenuRegistry.openMenu(player as ServerPlayer, object : MenuProvider {
val sp = player as ServerPlayer
val ent = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
MenuRegistry.openExtendedMenu(sp, object : ExtendedMenuProvider {
override fun getDisplayName(): Component = Component.literal("SCREEEEEN!")
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
return Menus.SCREEN_MENU.get().create(i, inventory);
// return Menus.SCREEN_MENU.get().create(i, inventory);
return ScreenMenu(i, inventory, ent)
}
override fun saveExtraData(buf: FriendlyByteBuf?) {
buf!!.writeBlockPos(blockPos)
}
})
}
return InteractionResult.SUCCESS
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
builder.add(FACING_HORIZ)
builder.add(FACING_VERTI)
}
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
val horiz = context.horizontalDirection
val looking = context.player!!.lookAngle
val biggest = max(max(abs(looking.y), abs(looking.z)), abs(looking.x))
return super.getStateForPlacement(context)!!
.setValue(FACING_HORIZ, horiz.opposite)
.setValue(FACING_VERTI, if (biggest != abs(looking.y)) 1 else if (looking.y < 0) 2 else 0 )
// val dirs = context.nearestLookingDirections
// context.
// return when (face) {
// Direction.UP -> super.getStateForPlacement(context)!!.setValue(FACING_HORIZ, looking.opposite).setValue(FACING_VERTI, 2)
// Direction.DOWN -> super.getStateForPlacement(context)!!.setValue(FACING_HORIZ, looking.opposite).setValue(FACING_VERTI, 0)
// else -> super.getStateForPlacement(context)!!.setValue(FACING_HORIZ, looking.opposite).setValue(FACING_VERTI, 1)
// }
}
}

View File

@@ -0,0 +1,111 @@
package org.neoflock.neocomputers.block.model
import com.mojang.blaze3d.vertex.VertexConsumer
import net.minecraft.client.Minecraft
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.renderer.block.model.BakedQuad
import net.minecraft.client.renderer.block.model.ItemOverrides
import net.minecraft.client.renderer.block.model.ItemTransforms
import net.minecraft.client.renderer.texture.TextureAtlas
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.resources.model.BakedModel
import net.minecraft.client.resources.model.Material
import net.minecraft.client.resources.model.ModelBaker
import net.minecraft.client.resources.model.ModelState
import net.minecraft.client.resources.model.UnbakedModel
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.RandomSource
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers
import java.util.function.Function
abstract class AbstractModel : BakedModel {
val atlas = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS)
var baker: ModelBaker? = null;
var mesh: Map<Direction, List<BakedQuad>> = mapOf(
Direction.UP to listOf(),
Direction.DOWN to listOf(),
Direction.EAST to listOf(),
Direction.WEST to listOf(),
Direction.SOUTH to listOf(),
Direction.NORTH to listOf(),
);
override fun getQuads(state: BlockState?, direction: Direction?, random: RandomSource): List<BakedQuad?>? { // perf? what's that?
if (direction != null) {
return mesh[direction]
} else {
var allquads: MutableList<BakedQuad> = mutableListOf()
mesh.forEach { (_, quads) ->
allquads.addAll(quads)
}
return allquads
}
}
fun _bake(spriteGetter: Function<Material, TextureAtlasSprite>): BakedModel {
bake { r: Material -> spriteGetter.apply(r) }
return this
}
override fun getParticleIcon(): TextureAtlasSprite? {
return atlas.apply(particle())
}
abstract fun bake(atlas: (Material) -> TextureAtlasSprite)
abstract fun particle(): ResourceLocation
}
class SchizoConsumer {
var mesh: Map<Direction, MutableList<BakedQuad>> = mapOf(
Direction.UP to mutableListOf(),
Direction.DOWN to mutableListOf(),
Direction.EAST to mutableListOf(),
Direction.WEST to mutableListOf(),
Direction.SOUTH to mutableListOf(),
Direction.NORTH to mutableListOf(),
);
var vertices: MutableList<Int> = mutableListOf();
var sprite: TextureAtlasSprite? = null;
var normal: Direction? = null;
var tint_index = -1
var shade = false
fun startQuad(normal: Direction, sprite: TextureAtlasSprite, tint_index: Int = -1, shade: Boolean = false): SchizoConsumer {
this.sprite = sprite
this.normal = normal
this.tint_index = tint_index
this.shade = shade
return this
}
fun vertex(x: Float, y: Float, z:Float, colorABGR: Int, u: Float, v: Float) { // uv are normalized
if (sprite == null || normal == null) {
throw Error("quad not started")
}
// NeoComputers.LOGGER.info("{} {} -> {} {} ", u, v, sprite!!.getU(u), sprite!!.getV(v))
vertices.add(x.toBits())
vertices.add(y.toBits())
vertices.add(z.toBits())
vertices.add(colorABGR)
vertices.add(sprite!!.getU(u).toBits())
vertices.add(sprite!!.getV(v).toBits())
vertices.add(0)
vertices.add(0)
if (vertices.size == 4 * 8) {
var quad = BakedQuad(vertices.toIntArray(), tint_index, normal!!, sprite!!, shade)
mesh.get(normal)!!.add(quad)
normal = null
sprite = null
vertices.clear()
}
} // TODO: add stuff like squares
}

View File

@@ -0,0 +1,83 @@
package org.neoflock.neocomputers.block.model
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.block.model.BakedQuad
import net.minecraft.client.renderer.block.model.ItemOverrides
import net.minecraft.client.renderer.block.model.ItemTransforms
import net.minecraft.client.renderer.texture.TextureAtlas
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.resources.model.BakedModel
import net.minecraft.client.resources.model.Material
import net.minecraft.client.resources.model.ModelBaker
import net.minecraft.client.resources.model.ModelState
import net.minecraft.client.resources.model.UnbakedModel
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.RandomSource
import net.minecraft.util.ResourceLocationPattern
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers
import java.util.function.Function
class RobotModel() : AbstractModel() {
val size = 0.4f
val l = 0.5f-size;
val h = 0.5f+size;
// TODO: fix dimensions (i eyeballed it)
override fun bake(atlas: (Material) -> TextureAtlasSprite) {
// override fun bake(atlas: Function<Material?, TextureAtlasSprite?>, state: ModelState): BakedModel {
val sprite = atlas(Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/robot")))
// val sprite = atlas.apply(Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "robot")))!!
var verts: SchizoConsumer = SchizoConsumer()
// top pyramid, enjoy reading this schizo mess
bakeTri(verts, Direction.WEST, sprite, l,l, l, h, 9f/16f,1f, 0f, 0f, 0f, 16f/32f)
bakeTri(verts, Direction.EAST, sprite, h, h, h, l, 9f/16f, 1f, 16/32f, 16/32f, 16/32f, 0f)
bakeTri(verts, Direction.SOUTH, sprite, l, h, h, h, 9f/16f, 1f, 0f, 16f/32f, 16f/32f, 16f/32f)
bakeTri(verts, Direction.NORTH, sprite, h, l, l,l, 9f/16f, 1f, 16/32f, 0f, 0f, 0f)
verts.startQuad(Direction.DOWN, sprite, 0)
verts.vertex(l, 9f/16F, h, 0xFFFFFFFF.toInt(), 16f/32f, 16/32F)
verts.vertex(l, 9f/16f, l, 0xFFFFFFFF.toInt(), 0F, 16/32F)
verts.vertex(h, 9f/16f, l, 0xFFFFFFFF.toInt(), 0F, 1F)
verts.vertex(h, 9f/16f, h, 0xFFFFFFFF.toInt(), 16/32F, 1F)
// bottom
bakeTri(verts, Direction.WEST, sprite, l, h, l, l, 8f/16f, 1/16f, 0f, 0f, 0f, 16f/32f)
bakeTri(verts, Direction.EAST, sprite, h, l, h, h, 8f/16f, 1/16f, 0f, 0f, 0f, 16f/32f)
bakeTri(verts, Direction.SOUTH, sprite, h, h, l, h, 8f/16f, 1/16f, 0f, 0f, 0f, 16f/32f)
bakeTri(verts, Direction.NORTH, sprite, l, l, h,l, 8f/16f, 1/16f, 0f, 0f, 0f, 16f/32f)
verts.startQuad(Direction.UP, sprite, 0)
verts.vertex(h, 8f/16F, l, 0xFFFFFFFF.toInt(), 16/32F, 16/32F)
verts.vertex(l, 8f/16f, l, 0xFFFFFFFF.toInt(), 0F, 16/32F)
verts.vertex(l, 8f/16f, h, 0xFFFFFFFF.toInt(), 0F, 1F)
verts.vertex(h, 8f/16f, h, 0xFFFFFFFF.toInt(), 16/32F, 1F)
this.mesh = verts.mesh
}
fun bakeTri(verts: SchizoConsumer, normal: Direction, sprite: TextureAtlasSprite, lx: Float, lz: Float, rx: Float, rz: Float, dy: Float, uy: Float, lu: Float, lv: Float, ru: Float, rv: Float) {
verts.startQuad(normal, sprite, 0)
verts.vertex(0.5F, uy, 0.5F, 0xFFFFFFFF.toInt(), 8F/32F, 8F/32F)
verts.vertex(0.5F, uy, 0.5F, 0xFFFFFFFF.toInt(), 8F/32F, 8F/32F)
verts.vertex(lx, dy, lz, 0xFFFFFFFF.toInt(), lu, lv)
verts.vertex(rx, dy, rz, 0xFFFFFFFF.toInt(), ru, rv)
}
override fun particle(): ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/teto")
override fun useAmbientOcclusion(): Boolean = true
override fun isGui3d(): Boolean = true
override fun usesBlockLight(): Boolean = true
override fun isCustomRenderer(): Boolean = false
override fun getTransforms(): ItemTransforms? = ItemTransforms.NO_TRANSFORMS
override fun getOverrides(): ItemOverrides? = ItemOverrides.EMPTY // TODO: item
}

View File

@@ -4,15 +4,19 @@ import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider
import net.minecraft.data.models.BlockModelGenerators
import net.minecraft.data.models.ItemModelGenerators
import net.minecraft.data.models.model.ModelTemplate
import net.minecraft.data.models.model.ModelTemplates
import org.neoflock.neocomputers.block.Blocks
import org.neoflock.neocomputers.item.Items
class ModelGenerator(output: FabricDataOutput) : FabricModelProvider(output) {
override fun generateBlockStateModels(blockStateModelGenerator: BlockModelGenerators) {
blockStateModelGenerator.createGenericCube(Blocks.CASE_BLOCK.get())
// blockStateModelGenerator.createGenericCube(Blocks.CASE_BLOCK.get())
}
override fun generateItemModels(itemModelGenerator: ItemModelGenerators) {
// itemModelGenerator.generateFlatItem(Items.SERVER0.get(), ModelTemplates.FLAT_ITEM)
}
}

View File

@@ -0,0 +1,8 @@
package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
class AssemblerEntity(pos: BlockPos, state: BlockState) : BlockEntity(BlockEntities.ASSEMBLER_ENTITY.get(), pos, state) {
}

View File

@@ -15,6 +15,7 @@ import org.neoflock.neocomputers.block.CapacitorEntity
import org.neoflock.neocomputers.block.CapacitorEntityTier1
import org.neoflock.neocomputers.block.CapacitorEntityTier2
import org.neoflock.neocomputers.block.CapacitorEntityTier3
import org.neoflock.neocomputers.block.RelayEntity
import org.neoflock.neocomputers.network.PowerManager
// complete fucking bullshit btw
@@ -42,50 +43,81 @@ object BlockEntities {
val SCREEN_ENTITY: RegistrySupplier<BlockEntityType<ScreenEntity>> = BLOCKENTITIES.register("screen") {
BlockEntityType(
::ScreenEntity, mutableSetOf(Blocks.SCREEN_BLOCK.get()), BullshitFix()
::ScreenEntity, setOf(Blocks.SCREEN_BLOCK.get()), BullshitFix()
)
}
val CAPACITOR_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier1>> = BLOCKENTITIES.register("capacitor") {
BlockEntityType(
::CapacitorEntityTier1, mutableSetOf(Blocks.CAPACITOR_BLOCK.get()), BullshitFix()
::CapacitorEntityTier1, setOf(Blocks.CAPACITOR_BLOCK.get()), BullshitFix()
)
}
val CAPACITOR2_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier2>> = BLOCKENTITIES.register("capacitor2") {
BlockEntityType(
::CapacitorEntityTier2, mutableSetOf(Blocks.CAPACITOR_BLOCK2.get()), BullshitFix()
::CapacitorEntityTier2, setOf(Blocks.CAPACITOR_BLOCK2.get()), BullshitFix()
)
}
val CAPACITOR3_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier3>> = BLOCKENTITIES.register("capacitor3") {
BlockEntityType(
::CapacitorEntityTier3, mutableSetOf(Blocks.CAPACITOR_BLOCK3.get()), BullshitFix()
::CapacitorEntityTier3, setOf(Blocks.CAPACITOR_BLOCK3.get()), BullshitFix()
)
}
val SOLARGEN_ENTITY: RegistrySupplier<BlockEntityType<SolarGeneratorBlockEntity>> = BLOCKENTITIES.register("solargen") {
BlockEntityType(
::SolarGeneratorBlockEntity, mutableSetOf(Blocks.SOLARGEN_BLOCK.get()), BullshitFix()
::SolarGeneratorBlockEntity, setOf(Blocks.SOLARGEN_BLOCK.get()), BullshitFix()
)
}
val COMBUSTGEN_ENTITY: RegistrySupplier<BlockEntityType<CombustionGeneratorBlockEntity>> = BLOCKENTITIES.register("combustgen") {
BlockEntityType(
::CombustionGeneratorBlockEntity, mutableSetOf(Blocks.COMBUSTGEN_BLOCK.get()), BullshitFix()
::CombustionGeneratorBlockEntity, setOf(Blocks.COMBUSTGEN_BLOCK.get()), BullshitFix()
)
}
val REDSTONEIO_ENTITY: RegistrySupplier<BlockEntityType<CombustionGeneratorBlockEntity>> = BLOCKENTITIES.register("redio") {
BlockEntityType(
::CombustionGeneratorBlockEntity, mutableSetOf(Blocks.REDSTONEIO_BLOCK.get()), BullshitFix()
::CombustionGeneratorBlockEntity, setOf(Blocks.REDSTONEIO_BLOCK.get()), BullshitFix()
)
}
val CASE_ENTITY: RegistrySupplier<BlockEntityType<CaseBlockEntity>> = BLOCKENTITIES.register("case") {
BlockEntityType(
::CaseBlockEntity, mutableSetOf(Blocks.CASE_BLOCK.get()), BullshitFix()
::CaseBlockEntity, setOf(Blocks.CASE_BLOCK.get()), BullshitFix()
)
}
val CABLE_ENTITY: RegistrySupplier<BlockEntityType<CableEntity>> = BLOCKENTITIES.register("cable") {
BlockEntityType(
::CableEntity, setOf(Blocks.CABLE_BLOCK.get()), BullshitFix()
)
}
val RELAY_ENTITY: RegistrySupplier<BlockEntityType<RelayEntity>> = BLOCKENTITIES.register("relay") {
BlockEntityType(
::RelayEntity, setOf(Blocks.RELAY_BLOCK.get()), BullshitFix()
)
}
val ROBOT_ENTITY: RegistrySupplier<BlockEntityType<RobotEntity>> = BLOCKENTITIES.register("robot") {
BlockEntityType(
::RobotEntity, setOf(Blocks.ROBOT_BLOCK.get()), BullshitFix()
)
}
val RACK_ENTITY: RegistrySupplier<BlockEntityType<RackEntity>> = BLOCKENTITIES.register("rack") {
BlockEntityType(
::RackEntity, setOf(Blocks.RACK_BLOCK.get()), BullshitFix()
)
}
val ASSEMBLER_ENTITY: RegistrySupplier<BlockEntityType<AssemblerEntity>> = BLOCKENTITIES.register("assembler") {
BlockEntityType(
::AssemblerEntity, setOf(Blocks.ASSEMBLER_BLOCK.get()), BullshitFix()
)
}
fun registerPowerBlocks() {
PowerManager.registerPowerBlockEntity(CAPACITOR_ENTITY.get())
PowerManager.registerPowerBlockEntity(CAPACITOR2_ENTITY.get())
PowerManager.registerPowerBlockEntity(CAPACITOR3_ENTITY.get())
PowerManager.registerPowerBlockEntity(SOLARGEN_ENTITY.get())
PowerManager.registerPowerBlockEntity(COMBUSTGEN_ENTITY.get())
PowerManager.registerPowerDevice(CAPACITOR_ENTITY.get())
PowerManager.registerPowerDevice(CAPACITOR2_ENTITY.get())
PowerManager.registerPowerDevice(CAPACITOR3_ENTITY.get())
PowerManager.registerPowerDevice(SOLARGEN_ENTITY.get())
PowerManager.registerPowerDevice(COMBUSTGEN_ENTITY.get())
PowerManager.registerPowerDevice(CASE_ENTITY.get())
}
}

View File

@@ -0,0 +1,47 @@
package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.DyeColor
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.CableBlock
import org.neoflock.neocomputers.block.CableBlock.Companion.COLOR
import org.neoflock.neocomputers.block.CableBlock.Companion.getPropByDirection
import org.neoflock.neocomputers.block.DeviceBlockEntity
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.network.DeviceNode
class CableEntity(pos: BlockPos, state: BlockState) : SingleDeviceBlockEntity(BlockEntities.CABLE_ENTITY.get(), pos, state) {
override val deviceNode = object : DeviceNode(){}
override fun sendCommitsToClient(level: Level) {
// we have nothing to commit lol
return
}
override fun requestServerState() {
// no state, we don't bother
return
}
override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? {
if(CableBlock.shouldConnect(blockPos, blockPos.relative(directionToRequester), level!!)) {
return deviceNode
}
return null
}
override fun setChanged() {
super.setChanged()
for (dir in Direction.entries) {
val ent = level!!.getBlockEntity(blockPos.relative(dir))
level!!.setBlockAndUpdate(blockPos, blockState.setValue(getPropByDirection(dir), CableBlock.shouldConnect(blockPos, blockPos.relative(dir), level!!)))
if(ent is DeviceBlockEntity) {
ent.connectionsAreDirty = true
}
}
}
}

View File

@@ -1,10 +1,7 @@
package org.neoflock.neocomputers.entity
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.BiomeAmbientSoundsHandler
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
import net.minecraft.client.resources.sounds.SoundInstance
import net.minecraft.client.sounds.LoopingAudioStream
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
@@ -24,52 +21,101 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.CaseBlock
import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.block.dirToIdx
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.gui.menu.CaseMenu
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.NNComponent
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.sounds.ComputerRunningSoundInstance
import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.utils.GenericContainer
import java.time.Duration
import kotlin.math.max
import kotlin.math.min
import org.neoflock.neocomputers.network.NodeSynchronizer
import kotlin.text.ifEmpty
class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider {
class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider {
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY)
var isOn = false
var diskActivityTime = 0 // TOOD: writing writers and reading readers
var networkActivityTime = 0
var err: String? = null
var arch = "Lua 5.3"
var soundInstance: SoundInstance? = null
override val node = object : Networking.Node() {
override var powerRole = PowerRole.STORAGE
override val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.CONSUMER
override var energyCapacity: Long = 500
}
override fun encodeDownstreamData(packet: FriendlyByteBuf) {
super.encodeDownstreamData(packet)
packet.writeBoolean(isOn)
}
override fun syncWithUpstream(packet: FriendlyByteBuf) {
super.syncWithUpstream(packet)
setRunning(packet.readBoolean())
}
override fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) {
val c = packet.readByte().toInt()
if(c == 0x01) {
start()
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
super.writeFullStateCommit(buf)
buf.writeUUID(address)
buf.writeBoolean(isOn)
buf.writeVarInt(diskActivityTime)
buf.writeVarInt(networkActivityTime)
buf.writeUtf(err ?: "")
}
if(c == 0x02) {
stop()
}
}
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
super.encodeScreenData(player, packet)
packet.writeBoolean(isOn)
override fun processCommit(buf: FriendlyByteBuf) {
super.processCommit(buf)
Networking.changeNodeAddress(this, buf.readUUID())
setRunning(buf.readBoolean())
diskActivityTime = buf.readVarInt()
networkActivityTime = buf.readVarInt()
err = buf.readUtf().ifEmpty { null }
}
override fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) {
super.processScreenInteraction(player, buf)
val c = buf.readByte().toInt()
if(c == 0x01) {
start()
}
if(c == 0x02) {
stop()
}
}
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
super.encodeScreenData(player, buf)
buf.writeBoolean(isOn)
buf.writeByteArray((err ?: "").encodeToByteArray())
buf.writeLong(energy)
buf.writeLong(energyCapacity)
buf.writeLong(getMachineMemoryUsed())
buf.writeLong(getMachineMemoryTotal())
buf.writeLong(getMachineComponentsUsed())
buf.writeLong(getMachineComponentsTotal())
buf.writeUtf(arch)
}
override fun tick() {
super.tick()
if (isRunning()) {
if(diskActivityTime > 0) diskActivityTime--
if(networkActivityTime > 0) networkActivityTime--
if(getMachineArchitectures().isEmpty()) {
crash("@neocomputers.errors.ENOCPU")
} else if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
crash("@neocomputers.errors.E2BIG")
} else if (!consumeEnergy(1)) {
crash("@neocomputers.errors.ENOENJ")
}
}
}
override fun received(message: Networking.Message) {
super.received(message)
if(message is Networking.ClassicPacket) {
NeoComputers.LOGGER.info("machine $address got $message from ${message.sender.address}")
}
}
override fun getComponent() = NNComponent("computer")
}
val redstoneIn = Array(Direction.entries.size) {0}
@@ -78,7 +124,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
fun refetchRedstone(dir: Direction) {
val src = blockPos.offset(dir.stepX, dir.stepY, dir.stepZ)
val cur = level?.getSignal(src, dir) ?: 0
val idx = dirToIdx(dir)
val idx = dir.ordinal
if(redstoneIn[idx] != cur) {
onRedstoneSignalChanged(dir, redstoneIn[idx], cur)
}
@@ -96,11 +142,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
item.onMachineEvent(it, this, event)
}
}
Networking.emitMessage(deviceNode, Networking.ComputerEvent(deviceNode, event))
}
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
sendMachineEvent(MachineRedstoneEvent(this, dir, oldValue, newValue))
Networking.emitMessage(node, Networking.ComputerUncheckedSignal(node, "redstone_changed", arrayOf(node.address.toString(), dirToIdx(dir), oldValue, newValue)))
Networking.emitMessage(deviceNode, Networking.ComputerUncheckedSignal(deviceNode, "redstone_changed", arrayOf(deviceNode.address.toString(), dir.ordinal, oldValue, newValue)))
NeoComputers.LOGGER.info("redstone in direction ${dir.name} changed from $oldValue to $newValue")
if(oldValue == 0) {
// Rising edge
@@ -116,7 +163,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
fun setRunning(value: Boolean) {
if(isOn == value) return
NeoComputers.LOGGER.info("[${node.address}] Going from $isOn to $value")
deviceNode.markChanged()
NeoComputers.LOGGER.info("[${deviceNode.address}] Going from $isOn to $value")
isOn = value
val world = level ?: return
blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn)
@@ -131,64 +179,117 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
return
}
// Server-side stuff!!
world.onBlockStateChange(blockPos, blockState, blockState)
sendMachineEvent(MachinePowerEvent(this, isOn))
Networking.emitMessage(deviceNode, Networking.ClassicPacket(deviceNode, deviceNode.address.toString(), null, 1, listOf(), 0))
}
override fun start(): Boolean {
if(isOn) return true
err = null
val architectures = getMachineArchitectures()
// Beep patterns taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/server/machine/Machine.scala
if(architectures.isEmpty()) {
crash("@neocomputers.errors.ENOCPU")
beepAsync("-..")
return false
}
if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
crash("@neocomputers.errors.E2BIG")
beepAsync("-..")
return false
}
// less than 20% energy is bad
if(deviceNode.energy < deviceNode.energyCapacity/5) {
crash("@neocomputers.errors.ENOENJ")
// we add a beep for the special case where we do have a little bit of energy :P
if(deviceNode.energy > 0) beepAsync("..")
return false
}
if(getMachineMemoryTotal() == 0L) {
crash("@neocomputers.errors.ENOMEM")
beepAsync("-.")
return false
}
if(arch !in architectures) {
// Just pick one! TODO: consult EEPROM first
arch = architectures.first()
}
beepAsync(".")
setRunning(true)
return isOn
}
override fun stop(): Boolean {
if(!isOn) return false
setRunning(false)
return isOn
}
override fun crash(error: String): Boolean {
NeoComputers.LOGGER.warn("Crashing cases is not implemented yet lol")
return false
beepAsync("--")
sendMachineEvent(MachineCrashEvent(this, error))
setRunning(false)
err = error
return true
}
override fun getLastError(): String? = null
override fun getLastError(): String? = err
override fun getMachineNode(): Networking.Node = node
override fun getMachineNode() = deviceNode
override fun getRedstoneInput(direction: Direction): Int = redstoneIn[dirToIdx(direction)]
override fun getRedstoneInput(direction: Direction): Int = redstoneIn[direction.ordinal]
override fun getRedstoneOutput(direction: Direction): Int = redstoneOut[dirToIdx(direction)]
override fun getRedstoneOutput(direction: Direction): Int = redstoneOut[direction.ordinal]
override fun setRedstoneOutput(direction: Direction, newValue: Int): Int {
val idx = dirToIdx(direction)
val idx = direction.ordinal
val old = redstoneOut[idx]
redstoneOut[idx] = newValue
return old
}
override fun beepAsync(frequency: Int, duration: Duration, volume: Double): Boolean {
NeoComputers.LOGGER.warn("beep not yet implemented")
override fun beepAsync(pattern: String, frequency: Int, duration: Duration, volume: Double): Boolean {
NodeSynchronizer.emitBeep(level!!, NodeSynchronizer.BeepDataPayload(getMachineBlockPosition(), pattern, frequency, duration, volume))
return true
}
override fun signalDiskActivity(delay: Int) {
diskActivityTime = max(delay, diskActivityTime)
}
override fun signalNetworkActivity(delay: Int) {
networkActivityTime = max(delay, networkActivityTime)
}
override fun getMachineMemoryTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getMemoryCapacity(it) }.sum().toLong()
override fun getMachineMemoryUsed(): Long = 0
override fun getMachineComponentsUsed(): Long = node.connections.size.toLong()
override fun getMachineComponentsUsed(): Long = deviceNode.getReachable().count { it.getComponent() != null }.toLong()
override fun getMachineComponentsTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getComponentCapacity(it) }.sum().toLong()
override fun getMachineArchitecture() = arch
override fun getMachineArchitectures() = stacks.mapNotNull { (it.item as? ComponentItem)?.getArchitecturesProvided(it) }.flatten().toSet()
override fun setMachineArchitecture(arch: String) {
if(this.arch == arch) return
this.arch = arch
if(isRunning()) {
stop()
start()
}
}
override fun getItems(): NonNullList<ItemStack> = stacks
override fun stillValid(player: Player): Boolean = true
override fun stillValid(player: Player): Boolean = !this.isRemoved
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
node.energy = min(node.energyCapacity, compoundTag.getLong("energy"))
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
deviceNode.energy = min(deviceNode.energyCapacity, tag.getLong("energy"))
//isOn = compoundTag.getBoolean("powerOn")
ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
ContainerHelper.loadAllItems(tag, getItems(), registries)
}
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy)
compoundTag.putLong("energy", deviceNode.energy)
//compoundTag.putBoolean("powerOn", isOn)
ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
}
@@ -196,15 +297,11 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
override fun getDisplayName(): Component? = Component.literal("Computer")
override fun createMenu(i: Int, inventory: Inventory, player: Player) = CaseMenu(i, inventory, this)
override fun canPlaceItem(i: Int, itemStack: ItemStack): Boolean = false
override fun canTakeItem(container: Container, i: Int, itemStack: ItemStack): Boolean = false
override fun setChanged() {
super.setChanged()
}
override fun setRemoved() {
setRunning(false)
super.setRemoved()
}
override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean = false
override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean = false
}

View File

@@ -1,6 +1,7 @@
package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
@@ -15,22 +16,27 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.CombustionGeneratorBlock
import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.utils.GenericContainer
import org.neoflock.neocomputers.utils.ContainerUtils
import kotlin.math.min
class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider {
class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : SingleDeviceBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider {
val energyPerTick: Long = 50
var burningTimeRemaining: Int = 0
override val node = object : Networking.Node() {
override val deviceNode = object : DeviceNode() {
override var powerRole = PowerRole.GENERATOR
override var energyCapacity: Long = 100000
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
buf.writeLong(energy)
buf.writeLong(energyCapacity)
}
}
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(1, ItemStack.EMPTY)
@@ -45,20 +51,20 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
return !this.isRemoved
}
override fun tickNode(level: Level) {
super.tickNode(level)
override fun tickDevice(level: Level) {
super.tickDevice(level)
// TODO: give us a block state tag for active
// keep combusting and shi
if(burningTimeRemaining > 0) {
burningTimeRemaining--
node.giveEnergy(energyPerTick)
deviceNode.giveEnergy(energyPerTick)
setChanged()
return
}
// no point
if(node.energy >= node.energyCapacity) return;
if(deviceNode.energy >= deviceNode.energyCapacity) return;
// :fire:
val fuel = stacks[0]
@@ -78,21 +84,16 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.COMBUSTGEN_ACTIVE, burningTimeRemaining > 0))
}
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
packet.writeLong(node.energy)
packet.writeLong(node.energyCapacity)
}
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
node.energy = min(node.energyCapacity, compoundTag.getLong("energy"))
deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))
burningTimeRemaining = compoundTag.getInt("burningTimeRemaining")
ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
}
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy)
compoundTag.putLong("energy", deviceNode.energy)
compoundTag.putInt("burningTimeRemaining", burningTimeRemaining)
ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
}

View File

@@ -3,8 +3,8 @@ package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.level.Level
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.Networking
import net.minecraft.world.phys.Vec3
import org.neoflock.neocomputers.network.DeviceNode
import java.time.Duration
abstract class MachineEvent {
@@ -13,13 +13,28 @@ abstract class MachineEvent {
data class MachineRedstoneEvent(override val machine: MachineEntity, val side: Direction, val oldValue: Int, val newValue: Int): MachineEvent()
data class MachinePowerEvent(override val machine: MachineEntity, val nowRunning: Boolean): MachineEvent()
data class MachineCrashEvent(override val machine: MachineEntity, val error: String): MachineEvent()
interface MachineEntity {
interface ComponentUser {
// Block position of machine, for wireless tech
fun getMachineBlockPosition(): BlockPos
fun getMachinePrecisePosition(): Vec3 = getMachineBlockPosition().center
fun getMachineLevel(): Level
fun beepAsync(frequency: Int, duration: Duration, volume: Double): Boolean
fun getMachineNode(): DeviceNode
}
interface MachineEntity: ComponentUser {
// Pattern can have dots (.), dashes (-) and spaces ( ).
// Each character is duration long, and has a 50ms break.
// For non-short ones, which are typically reserved only for hardware interactions,
// the duration is doubled.
// Architectures should only use short ones.
fun beepAsync(pattern: String, frequency: Int = 1000, duration: Duration = Duration.ofMillis(200), volume: Double = 1.0): Boolean
// Signals that disk activity is happening for at least that delay
fun signalDiskActivity(delay: Int)
// signal network activity to a machine
fun signalNetworkActivity(delay: Int)
fun isRunning(): Boolean
fun start(): Boolean
@@ -27,13 +42,14 @@ interface MachineEntity {
fun crash(error: String): Boolean
fun getLastError(): String?
fun getMachineNode(): Networking.Node
// Some metadata
fun getMachineMemoryTotal(): Long
fun getMachineMemoryUsed(): Long
fun getMachineComponentsUsed(): Long
fun getMachineComponentsTotal(): Long
fun getMachineArchitecture(): String
fun getMachineArchitectures(): Set<String>
fun setMachineArchitecture(arch: String)
// Redstone signals
fun getRedstoneInput(direction: Direction): Int

View File

@@ -0,0 +1,134 @@
package org.neoflock.neocomputers.entity
import dev.architectury.registry.menu.ExtendedMenuProvider
import net.minecraft.client.renderer.RenderType
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.ContainerHelper
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.ChestBlockEntity
import net.minecraft.world.level.block.entity.FurnaceBlockEntity
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.DeviceBlockEntity
import org.neoflock.neocomputers.gui.menu.RackMenu
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.ContainerUtils
import org.neoflock.neocomputers.utils.GenericContainer
class RackEntity(pos: BlockPos, state: BlockState) : DeviceBlockEntity(BlockEntities.RACK_ENTITY.get(), pos, state), ExtendedMenuProvider, GenericContainer {
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(4, ItemStack.EMPTY)
var conns = mutableListOf(
-1, -1, -1, -1,
-1, -1, -1, -1,
-1, -1, -1, -1,
-1, -1, -1, -1
)
var relayMode = false
val node: DeviceNode = object : DeviceNode() {
override var reachability: Networking.Visibility = Networking.Visibility.NONE
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
super.writeFullStateCommit(buf)
buf.writeBoolean(relayMode)
val tag = CompoundTag() // better way to do this, a better way i do not care to find atm
ContainerHelper.saveAllItems(tag, stacks, level!!.registryAccess())
buf.writeNbt(tag)
for (conn in conns) {
buf.writeInt(conn)
}
}
override fun processCommit(buf: FriendlyByteBuf) {
super.processCommit(buf)
relayMode = buf.readBoolean()
val tag = buf.readNbt()
ContainerHelper.loadAllItems(tag!!, stacks, level!!.registryAccess())
for (i in 0..15) {
conns[i] = buf.readInt()
}
markChanged()
setChanged()
}
override fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) {
super.processScreenInteraction(player, buf)
relayMode = buf.readBoolean()
val slot = buf.readInt()
conns[slot*4+0] = buf.readInt()
conns[slot*4+1] = buf.readInt()
conns[slot*4+2] = buf.readInt()
conns[slot*4+3] = buf.readInt()
markChanged()
setChanged()
}
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) { // TODO: set this up so other players can mess with racks
super.encodeScreenData(player, buf)
}
}
override fun setChanged() {
super.setChanged()
node.markChanged()
}
override fun getDisplayName(): Component? = Component.literal("Rack")
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
return RackMenu(i, inventory, this)
}
override fun getDeviceNodes(): List<DeviceNode> = listOf(node)
override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? = node
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
ContainerHelper.loadAllItems(tag, getItems(), registries)
relayMode = tag.getBoolean("relay")
val connarray = tag.getIntArray("conns")
if (connarray.size == 16) conns = connarray.toMutableList()
setChanged()
}
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
ContainerHelper.saveAllItems(tag, getItems(), registries)
tag.putBoolean("relay", relayMode)
tag.putIntArray("conns", conns.toIntArray())
}
// override fun getItems(): NonNullList<ItemStack> = items
override fun getItems(): NonNullList<ItemStack> = stacks
override fun stillValid(player: Player): Boolean = true
override fun saveExtraData(buf: FriendlyByteBuf?) {
buf!!.writeBlockPos(blockPos)
}
}

View File

@@ -0,0 +1,20 @@
package org.neoflock.neocomputers.entity
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
import net.minecraft.world.Nameable
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers
class RobotEntity(pos: BlockPos, state: BlockState) : BlockEntity(BlockEntities.ROBOT_ENTITY.get(), pos, state,), Nameable {
val body: ModelPart? = null
val name = "Diddyx" //TODO: names
override fun getName(): Component? = Component.literal(name)
init {
NeoComputers.LOGGER.info("yooo")
}
}

View File

@@ -1,13 +1,117 @@
package org.neoflock.neocomputers.entity;
package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos
import net.minecraft.locale.Language
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.gui.buffer.BufferRenderer
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.NNComponent
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.utils.GPUChar
import org.neoflock.neocomputers.utils.TextBuffer
import kotlin.text.ifEmpty
class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
NodeBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) {
SingleDeviceBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) {
override val node = Networking.Node()
var lastError: String? = null
var isOn: Boolean = false
override val deviceNode = object : DeviceNode() {
override fun received(message: Networking.Message) {
super.received(message)
if(message is Networking.ComputerEvent) {
val mEnv = message.machineEvent
NeoComputers.LOGGER.info("Got message $mEnv!")
if(mEnv is MachinePowerEvent) {
if(mEnv.nowRunning) {
lastError = null
textBuf.fill(0, 0, textBuf.width, textBuf.height, GPUChar(' '))
textBuf.set(0, 0, address.toString())
}
isOn = mEnv.nowRunning
markChanged()
}
if(mEnv is MachineCrashEvent) {
lastError = mEnv.error
markChanged()
}
}
}
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
super.encodeScreenData(player, buf)
textBuf.encodeContents(buf)
}
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
super.writeFullStateCommit(buf)
buf.writeUUID(address)
buf.writeBoolean(isOn)
buf.writeUtf(lastError ?: "")
textBuf.encodeContents(buf)
}
override fun processCommit(buf: FriendlyByteBuf) {
super.processCommit(buf)
if(Networking.changeNodeAddress(this, buf.readUUID())) createScreenTexture()
isOn = buf.readBoolean()
lastError = buf.readUtf().ifEmpty { null }
textBuf.decodeContents(buf)
}
override fun getComponent() = NNComponent("screen")
}
var bound = "screen/unbound"
val textBuf = TextBuffer(50, 16)
private var cleanRenderer: () -> Unit = { } // TODO: THIS SUCKS, FIND A BETTER WAY
override fun tickDevice(level: Level) {
super.tickDevice(level)
cleanRenderer()
createScreenTexture()
}
override fun setRemoved() {
super.setRemoved()
bound = "screen/unbound" // ensure no missing texture is displayed
cleanRenderer()
}
private fun createScreenTexture() {
bound = "screen/"+deviceNode.address.toString().replace("-", "_")
if (level!!.isClientSide) {
if(lastError == null) {
if(!isOn) {
textBuf.fill(0, 0, textBuf.width, textBuf.height)
}
val renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf)
renderer.drawBuffer()
cleanRenderer = { renderer.clean() }
} else {
var trueError = lastError!!
if(trueError.startsWith("@")) {
val trans = trueError.substring(1)
val lang = Language.getInstance()
trueError = lang.getOrDefault("neocomputers.computer.errorNoMsg", "Error: ") + lang.getOrDefault(trans)
}
val throwAwayBuf = TextBuffer(50, 16)
val fg = 0xFFFFFF
val bg = 0x2B68A6
throwAwayBuf.fill(0, 0, throwAwayBuf.width, throwAwayBuf.height, GPUChar(' ', fg, bg))
throwAwayBuf.set((throwAwayBuf.width - trueError.length) / 2, throwAwayBuf.height/2, trueError, fg, bg)
val renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), throwAwayBuf)
renderer.drawBuffer()
cleanRenderer = { renderer.clean() }
}
}
}
}

View File

@@ -1,37 +1,38 @@
package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.PowerRole
class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) {
val energyPerTick: Long = 50
class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : SingleDeviceBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) {
val energyPerTick: Long = 10
override val node = object : Networking.Node() {
override val deviceNode = object : DeviceNode() {
override var powerRole: PowerRole = PowerRole.GENERATOR
override var energyCapacity: Long = 50000
}
override fun tickNode(level: Level) {
super.tickNode(level)
override fun tickDevice(level: Level) {
super.tickDevice(level)
val l = level
if(l.isDay) {
node.giveEnergy(energyPerTick)
deviceNode.giveEnergy(energyPerTick)
}
}
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
node.energy = compoundTag.getLong("energy")
deviceNode.energy = compoundTag.getLong("energy")
}
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy)
compoundTag.putLong("energy", deviceNode.energy)
}
}

View File

@@ -0,0 +1,46 @@
package org.neoflock.neocomputers.entity.render
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexFormat
import com.mojang.math.Axis
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.AssemblerEntity
class AssemblerEntityRenderer(val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<AssemblerEntity> {
val RENDER_TYPE = {l: ResourceLocation ->
RenderType.create("nc_assembler", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.QUADS, RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder()
.setTextureState(RenderStateShard.TextureStateShard(l, false, false))
.setShaderState(RenderStateShard.ShaderStateShard.POSITION_TEX_SHADER)
.createCompositeState(false))
}
override fun render(blockEntity: AssemblerEntity, partialTick: Float, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
var buffer = bufferSource.getBuffer(RENDER_TYPE(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/block/assembler_top_on.png")))
val pose = poseStack.last()
buffer.addVertex(pose, 0f, 17/16f, 1f).setUv(1f, 0f)
buffer.addVertex(pose, 1f, 17/16f, 1f).setUv(1f, 1f)
buffer.addVertex(pose, 1f, 17/16f, 0f).setUv(0f, 1f)
buffer.addVertex(pose, 0f, 17/16f, 0f).setUv(0f, 0f)
// TODO: do assembling texture
buffer = bufferSource.getBuffer(RENDER_TYPE(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/block/assembler_side_on.png")))
poseStack.pushPose()
for (i in 0..3) {
val pose = poseStack.last()
buffer.addVertex(pose, 1.001f, 1f, 0f).setUv(0f, 0f)
buffer.addVertex(pose, 1.001f, 1f, 1f).setUv(1f, 0f)
buffer.addVertex(pose, 1.001f, 0f, 1f).setUv(1f, 1f)
buffer.addVertex(pose, 1.001f, 0f, 0f).setUv(0f, 1f)
poseStack.rotateAround(Axis.YP.rotationDegrees(90f), 0.5f, 0.5f, 0.5f)
}
poseStack.popPose()
}
}

View File

@@ -0,0 +1,69 @@
package org.neoflock.neocomputers.entity.render
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.blaze3d.vertex.VertexFormat
import com.mojang.math.Axis
import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.core.Direction
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.CaseBlock
import org.neoflock.neocomputers.entity.CaseBlockEntity
class CaseEntityRenderer(private val context: BlockEntityRendererProvider.Context?) :
BlockEntityRenderer<CaseBlockEntity> {
val OFF = 0xFF5F855E.toInt()
val GREEN = 0xFF4EDC5E.toInt()
val RED = 0xFFff102B.toInt()
val BLINKTIME: Long = 10 // in ticks
val RENDER_TYPE = RenderType.create("nc_case", DefaultVertexFormat.POSITION_COLOR_LIGHTMAP, VertexFormat.Mode.QUADS,
RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder()
.setShaderState(RenderStateShard.POSITION_COLOR_LIGHTMAP_SHADER)
.setLightmapState(RenderStateShard.LightmapStateShard.LIGHTMAP)
.createCompositeState(false))
override fun render(ent: CaseBlockEntity, partialTick: Float, mat: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
val buffer = bufferSource.getBuffer(RENDER_TYPE);
mat.pushPose()
handleDirection(ent.blockState.getValue(CaseBlock.FACING), mat)
mat.translate(5/16F, 14/16F, 0.0001F)
if (ent.isOn) drawLED(buffer, mat.last(), 3F)
else if (ent.getLastError() != null) { // if else hell
if ((ent.level!!.dayTime/BLINKTIME) % 2 == 1.toLong()) drawLED(buffer, mat.last(), 3F, RED)
else drawLED(buffer, mat.last(), 3F, OFF, packedLight)
} else drawLED(buffer, mat.last(), 3F, OFF, packedLight)
mat.translate(6/16F, 0F, 0F)
drawLED(buffer, mat.last(), 2F, if (ent.diskActivityTime > 0) GREEN else OFF, if (ent.diskActivityTime > 0) LightTexture.FULL_BRIGHT else packedLight)
mat.popPose()
}
private fun drawLED(buffer: VertexConsumer, mat: PoseStack.Pose, width: Float, color: Int = GREEN, light: Int = LightTexture.FULL_BRIGHT) {
buffer.addVertex(mat, width/16F, 0F, 0F).setColor(color).setLight(light)
buffer.addVertex(mat, width/16F, 1/16F, 0F).setColor(color).setLight(light)
buffer.addVertex(mat, 0F, 1/16F, 0F).setColor(color).setLight(light)
buffer.addVertex(mat, 0F, 0F, 0F).setColor(color).setLight(light)
}
private fun handleDirection(facing: Direction, mat: PoseStack) {
when (facing) {
Direction.SOUTH -> { mat.translate(0F, 0F, 1F) }
Direction.EAST -> { mat.mulPose(Axis.YP.rotationDegrees(90F)); mat.translate(-1F, 0F, 1F) }
Direction.WEST -> { mat.mulPose(Axis.YN.rotationDegrees(90F)); }
Direction.NORTH -> {mat.mulPose(Axis.YP.rotationDegrees(180F)); mat.translate(-1F, 0F, 0F) }
else -> return
}
}
}

View File

@@ -0,0 +1,15 @@
package org.neoflock.neocomputers.entity.render
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers
import org.neoflock.neocomputers.entity.BlockEntities
object EntityRenderers {
fun registerBlockEntityRenderers() {
BlockEntityRenderers.register(BlockEntities.SCREEN_ENTITY.get(), ::ScreenEntityRenderer); // TODO: put this in common
BlockEntityRenderers.register(BlockEntities.CASE_ENTITY.get(), ::CaseEntityRenderer);
BlockEntityRenderers.register(BlockEntities.RELAY_ENTITY.get(), ::RelayEntityRenderer);
BlockEntityRenderers.register(BlockEntities.ROBOT_ENTITY.get(), ::RobotEntityRenderer);
BlockEntityRenderers.register(BlockEntities.RACK_ENTITY.get(), ::RackEntityRenderer);
BlockEntityRenderers.register(BlockEntities.ASSEMBLER_ENTITY.get(), ::AssemblerEntityRenderer)
}
}

View File

@@ -0,0 +1,39 @@
package org.neoflock.neocomputers.entity.render
import com.mojang.authlib.minecraft.client.MinecraftClient
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.LevelRenderer
import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.block.ModelBlockRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.world.level.LightLayer
import net.minecraft.world.level.dimension.DimensionType
import net.minecraft.world.level.lighting.BlockLightEngine
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.RackEntity
import org.neoflock.neocomputers.item.RackItem
class RackEntityRenderer(val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<RackEntity> {
override fun render(ent: RackEntity, partialTick: Float, poseStack: PoseStack, source: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
poseStack.pushPose()
poseStack.translate(1/16f, 11/16f, 1/16f)
val items = ent.stacks
for (i in 0..3) {
if (items[i].item is RackItem) {
val item = items[i].item as RackItem
item.render(source, poseStack, packedLight, 2f+(3*i))
}
poseStack.translate(0f, -3/16f, 0f)
}
// val render_slot = (ent.level!!.dayTime/40)%4 // this is purely temporary type shit like true alpha shit, anyway it go to 0-3, change and test it if you want
// poseStack.translate(0f, (render_slot)*-3/16f, 0f)
// val server = object : RackItem { override fun render_lights(source: MultiBufferSource, stack: PoseStack, light: Int) { } }
// server.render(source, poseStack, packedLight, 2f+(3*render_slot)) // who knows atp
poseStack.popPose()
}
}

View File

@@ -0,0 +1,66 @@
package org.neoflock.neocomputers.entity.render
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexFormat
import com.mojang.math.Axis
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.RelayEntity
import java.util.Timer
import kotlin.math.min
class RelayEntityRenderer(val context: BlockEntityRendererProvider.Context?): BlockEntityRenderer<RelayEntity> {
val RELAY_ON_TEX = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/block/relay_side_on.png")
val RENDER_TYPE: RenderType =
RenderType.create(
"nc_screen",
DefaultVertexFormat.POSITION_TEX_COLOR,
VertexFormat.Mode.QUADS,
RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder().
setShaderState(RenderStateShard.ShaderStateShard(GameRenderer::getPositionTexColorShader)).
setTextureState(RenderStateShard.TextureStateShard(RELAY_ON_TEX, false, false))
.createCompositeState(false))
override fun render(
blockEntity: RelayEntity,
partialTick: Float,
mat: PoseStack,
bufferSource: MultiBufferSource,
packedLight: Int,
packedOverlay: Int
) {
if(blockEntity.activityTickLeft == 0) return
val alpha = min((blockEntity.activityTickLeft.toFloat() * 255 / 20).toInt(), 255)
for(i in 0..<4) {
mat.pushPose()
val antiZFight = 0.001F
mat.rotateAround(Axis.YN.rotationDegrees(90F * i), 0.5F, 0.5F, 0.5F)
mat.translate(0F, 0F, 1F + antiZFight)
val width = 1F
val height = 1F
val bx = 0F
val by = 0F
val buffer = bufferSource.getBuffer(RENDER_TYPE)
buffer.addVertex(mat.last(), bx + width, by, 0f).setUv(1f, 1f).setColor(alpha, alpha, alpha, alpha)
buffer.addVertex(mat.last(), bx + width, by + height, 0f).setUv(1f, 0f).setColor(alpha, alpha, alpha, alpha)
buffer.addVertex(mat.last(), bx, by + height, 0f).setUv(0f, 0f).setColor(alpha, alpha, alpha, alpha)
buffer.addVertex(mat.last(), bx, by, 0f).setUv(0f, 1f).setColor(alpha, alpha, alpha, alpha)
mat.popPose()
}
}
}

View File

@@ -0,0 +1,129 @@
package org.neoflock.neocomputers.entity.render
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.blaze3d.vertex.VertexFormat
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.renderer.ItemBlockRenderTypes
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.RenderType.CompositeState
import net.minecraft.client.renderer.block.ModelBlockRenderer
import net.minecraft.client.renderer.block.model.BakedQuad
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.client.renderer.blockentity.ChestRenderer
import net.minecraft.client.renderer.entity.EntityRenderer
import net.minecraft.client.renderer.entity.LivingEntityRenderer
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.renderer.texture.TextureAtlas
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.client.resources.model.BakedModel
import net.minecraft.client.resources.model.Material
import net.minecraft.client.resources.model.ModelResourceLocation
import net.minecraft.client.resources.model.ModelState
import net.minecraft.core.Direction
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.RandomSource
import net.minecraft.world.item.DyeColor
import net.minecraft.world.level.block.entity.ChestBlockEntity
import net.minecraft.world.phys.Vec3
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.model.RobotModel
import org.neoflock.neocomputers.entity.RobotEntity
import kotlin.math.sin
class RobotEntityRenderer(val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<RobotEntity> {
val atlas: (Material) -> TextureAtlasSprite = { m ->
Minecraft.getInstance().getTextureAtlas(m.atlasLocation()).apply(m.texture())
}
val loc: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "robot")
var model: BakedModel? = Minecraft.getInstance().modelManager.getModel(ModelResourceLocation.inventory(loc))
val renderer: ModelBlockRenderer = ModelBlockRenderer(Minecraft.getInstance().blockColors) // so ass
val STREAK_RENDER_TYPE = RenderType.create("nc_robot_streak", DefaultVertexFormat.POSITION_TEX_COLOR, VertexFormat.Mode.QUADS, RenderType.TRANSIENT_BUFFER_SIZE,
CompositeState.builder()
.setShaderState(RenderStateShard.ShaderStateShard { GameRenderer.getPositionTexColorShader() })
.setTransparencyState(RenderStateShard.ADDITIVE_TRANSPARENCY)
.setCullState(RenderStateShard.CullStateShard.NO_CULL)
.setTextureState(RenderStateShard.TextureStateShard(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/block/robot.png"), false, false))
.createCompositeState(false))
override fun render(ent: RobotEntity, partialTick: Float, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
poseStack.pushPose()
// poseStack.translate(0f, sin(ent.level!!.dayTime.toFloat()/20F)*0.02F, 0f)
val modelbuffer = bufferSource.getBuffer(RenderType.entitySolid(TextureAtlas.LOCATION_BLOCKS))
val colorbuffer = bufferSource.getBuffer(STREAK_RENDER_TYPE)
val col = DyeColor.LIGHT_GRAY.fireworkColor
val red = ((col and 0xFF0000) shr 8*2) / 255f
val green = ((col and 0xFF00) shr 8) / 255f
val blue = ((col and 0xFF)) / 255f
renderer.renderModel(poseStack.last(), modelbuffer, ent.blockState, model!!, red, green, blue, packedLight, packedOverlay)
renderLight(poseStack, colorbuffer, (ent.level!!.dayTime%16).toInt())
// TODO: crafting table and chest little models
// EntityRenderer
renderTag(ent, Component.literal(ent.name), poseStack, bufferSource, packedLight, partialTick)
poseStack.popPose()
}
// offset is 0-15
fun renderLight(poseStack: PoseStack, buffer: VertexConsumer, offset: Int) {
poseStack.pushPose()
val u1 = 0.5f
val v1 = 0.5f + offset*1/32f
val u2 = 1F
val v2 = 17/32f + offset*1/32f
for (i in 0..3) {
poseStack.rotateAround(Axis.YP.rotationDegrees(90f), 0.5f, 0.5f, 0.5f)
buffer.addVertex(poseStack.last(),0.1f+2/16f, 7/16f, 0.9f-2/16f).setColor(1f, 0f, 0f, 1f).setUv(u2, v2)
buffer.addVertex(poseStack.last(),0.1f+2/16f, 9/16f, 0.9f-2/16f).setColor(1f, 0f, 0f, 1f).setUv(u2, v1)
buffer.addVertex(poseStack.last(),0.1f+2/16f, 9/16f, 0.1f+2/16f).setColor(1f, 0f, 0f, 1f).setUv(u1, v1)
buffer.addVertex(poseStack.last(),0.1f+2/16f, 7/16f, 0.1f+2/16f).setColor(1f, 0f, 0f, 1f).setUv(u1, v2)
}
poseStack.popPose()
}
fun renderTag(ent: RobotEntity, name: Component, stack: PoseStack, source: MultiBufferSource, light: Int, ptick: Float) {
val d = Minecraft.getInstance().cameraEntity!!.distanceToSqr(ent.blockPos.center)
if (d > 4096.0) return
val vec = Vec3(0.5, 20 / 16.0, 0.5)
stack.pushPose()
stack.translate(vec.x, vec.y, vec.z)
stack.mulPose(context.entityRenderer.cameraOrientation())
stack.scale(0.025F, -0.025F, 0.025F)
val opacity = Minecraft.getInstance().options.getBackgroundOpacity(0.25F)
val alpha: Int = (opacity * 255.0f).toInt() shl 24
// val alpha = 255
val halfwidth = (-context.font.width(name)) / 2;
// RenderSystem.enableDepthTest()
// RenderSystem.enableBlend()
// RenderSystem.depthMask(true)
context.font.drawInBatch(name, halfwidth.toFloat(), 2f, 0xFFFFFF, false, stack.last().pose(), source, Font.DisplayMode.SEE_THROUGH, alpha, light)
(source as MultiBufferSource.BufferSource).endBatch()
stack.popPose()
}
}

View File

@@ -0,0 +1,71 @@
package org.neoflock.neocomputers.entity.render
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexFormat
import com.mojang.math.Axis
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.ScreenBlock
import org.neoflock.neocomputers.entity.ScreenEntity
class ScreenEntityRenderer(val context: BlockEntityRendererProvider.Context?) : BlockEntityRenderer<ScreenEntity> { // TODO: FORGE
val RENDER_TYPE: (ResourceLocation) -> RenderType = { t: ResourceLocation ->
RenderType.create(
"nc_screen",
DefaultVertexFormat.POSITION_TEX,
VertexFormat.Mode.QUADS,
RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder().
setShaderState(RenderStateShard.POSITION_TEX_SHADER).
setTextureState(RenderStateShard.TextureStateShard(t, false, false))
.createCompositeState(false))
}
override fun render(entity: ScreenEntity, partialTick: Float, mat: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
if(!entity.isOn && entity.lastError == null) return
val facing = entity.blockState.getValue(ScreenBlock.FACING_HORIZ)
val vert = entity.blockState.getValue(ScreenBlock.FACING_VERTI)-1
mat.pushPose()
handleDirection(facing, vert, mat)
mat.translate(2 / 16f, 2 / 16f, 0.0001f) // am i epstein or am i just retarded
val width = 3/4F
val height = 3/4F
val bx = 0F
val by = 0F
val rendertype = RENDER_TYPE(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, entity.bound))
val buffer = bufferSource.getBuffer(rendertype)
buffer.addVertex(mat.last(), bx+width, by, 0f).setUv(1f, 1f)
buffer.addVertex(mat.last(), bx+width, by+height, 0f).setUv(1f, 0f)
buffer.addVertex(mat.last(), bx, by+height, 0f).setUv(0f, 0f)
buffer.addVertex(mat.last(), bx, by, 0f).setUv(0f, 1f)
mat.popPose()
}
private fun handleDirection(facing: Direction, vert: Int, mat: PoseStack) { // TODO: separate up and down from cardinal directions
// mat.mulPose(Axis.XN.rotationDegrees(vert.toFloat()*90F))
// if (vert==0) {
// mat.mulPose(Axis.YP.rotationDegrees(90F))
// return
// }
when (facing) {
Direction.SOUTH -> { mat.translate(0F, 0F, 1F) }
Direction.EAST -> { mat.mulPose(Axis.YP.rotationDegrees(90F)); mat.translate(-1F, 0F, 1F) }
Direction.WEST -> { mat.mulPose(Axis.YN.rotationDegrees(90F)); }
Direction.NORTH -> {mat.mulPose(Axis.YP.rotationDegrees(180F)); mat.translate(-1F, 0F, 0F) }
else -> {}
// Direction.UP -> { mat.mulPose(Axis.XN.rotationDegrees(90F)); mat.mulPose(Axis.ZP.rotationDegrees(180F)); mat.translate(-1F, 0F, 1F) } // idek
// Direction.DOWN -> { mat.mulPose(Axis.XP.rotationDegrees(90F)); mat.mulPose(Axis.ZN.rotationDegrees(180F)); mat.translate(-1F, -1F, 0F) }
}
mat.mulPose(Axis.XN.rotationDegrees(vert*90F))
mat.translate(0F, if (vert==-1) -1F else 0F, if (vert==1) 1F else 0F)
}
}

View File

@@ -0,0 +1,29 @@
package org.neoflock.neocomputers.gui.menu
import net.fabricmc.fabric.mixin.item.client.HeldItemRendererMixin
import net.minecraft.client.gui.MapRenderer
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.block.Blocks
import org.neoflock.neocomputers.gui.widget.DynamicSlot
import org.neoflock.neocomputers.utils.GenericContainer
import org.neoflock.neocomputers.utils.GenericContainerMenu
class CaseSlot(container: Container, index: Int, x: Int, y: Int) : DynamicSlot(container, index, x, y) {
val caseItems = listOf(Blocks.CASE_BLOCK.get().asItem())
override fun mayPlace(stack: ItemStack): Boolean = stack.item in caseItems
}
class AssemblerMenu : GenericContainerMenu {
constructor(id: Int, inv: Inventory) : this(id, inv, SimpleContainer(1))
constructor(id: Int, inv: Inventory, container: Container) : super(Menus.ASSEMBLER_MENU.get(), id, container) {
this.addSlot(CaseSlot(container, 0, 12, 12))
this.addInventoryHotbar(inv, 8, 84)
}
}

View File

@@ -20,20 +20,20 @@ open class CaseMenu : GenericContainerMenu {
constructor(i: Int, inv: Inventory) : this(i, inv, SimpleContainer(7))
constructor(i: Int, inv: Inventory, container: Container) : super(Menus.CASE_MENU.get(), i, container) {
this.addInventorySlots(inv, 8, 84)
val machine = container as? CaseBlockEntity
this.addSlot(ComponentSlot(this.container, 0, 20, 34, machine, eepromRequirement))
this.addSlot(ComponentSlot(container, 0, 20, 34, machine, eepromRequirement))
var i = 1
for ((col, slotCol) in slotRequirements.withIndex()) {
for ((row, slotReq) in slotCol.withIndex()) {
this.addSlot(ComponentSlot(this.container, i, 98+(col*22), 18*(row+1)-2, machine, slotReq))
this.addSlot(ComponentSlot(container, i, 98+(col*22), 18*(row+1)-2, machine, slotReq))
i++
}
}
this.addInventorySlots(inv, 8, 84)
// for (int col=1; col<4; col++) {
// for (int row=1; row<4; row++) {
// int i = (row-1)*3+(col-1);

View File

@@ -10,20 +10,30 @@ import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.gui.menu.ScreenMenu
import org.neoflock.neocomputers.gui.screen.AssemblerScreen
import org.neoflock.neocomputers.gui.screen.CaseScreen
import org.neoflock.neocomputers.gui.screen.CombustionGeneratorScreen
import org.neoflock.neocomputers.gui.screen.RackScreen
import org.neoflock.neocomputers.gui.screen.RelayScreen
import org.neoflock.neocomputers.gui.screen.ScreenScreen
object Menus {
val MENUS: DeferredRegister<MenuType<*>> = DeferredRegister.create(NeoComputers.MODID, Registries.MENU)
val SCREEN_MENU: RegistrySupplier<MenuType<ScreenMenu>> = MENUS.register("screen_menu") { MenuType(::ScreenMenu, FeatureFlagSet.of()) }
val SCREEN_MENU: RegistrySupplier<MenuType<ScreenMenu>> = MENUS.register("screen_menu") { MenuRegistry.ofExtended<ScreenMenu>(::ScreenMenu) }
val COMBUSTGEN_MENU: RegistrySupplier<MenuType<CombustionGeneratorMenu>> = MENUS.register("combustgen_menu") { MenuType(::CombustionGeneratorMenu, FeatureFlagSet.of() ) }
val CASE_MENU: RegistrySupplier<MenuType<CaseMenu>> = MENUS.register("case_menu") { MenuType(::CaseMenu, FeatureFlagSet.of() )}
val RELAY_MENU: RegistrySupplier<MenuType<RelayMenu>> = MENUS.register("relay_menu") { MenuType(::RelayMenu, FeatureFlagSet.of() )}
val RACK_MENU: RegistrySupplier<MenuType<RackMenu>> = MENUS.register("rack_menu") { MenuRegistry.ofExtended(::RackMenu) }
val ASSEMBLER_MENU: RegistrySupplier<MenuType<AssemblerMenu>> = MENUS.register("assembler_menu") { MenuType(::AssemblerMenu, FeatureFlagSet.of() )}
// val RACK_MENU: RegistrySupplier<MenuType<RackMenu>> = MENUS.register("rack_menu") { MenuType(::RackMenu, FeatureFlagSet.of() )}
fun registerScreens() {
MenuScreens.register(Menus.COMBUSTGEN_MENU.get()) { m: CombustionGeneratorMenu, u, comp ->CombustionGeneratorScreen(m,u,comp)}
MenuScreens.register(Menus.SCREEN_MENU.get(), ::ScreenScreen)
MenuScreens.register(Menus.CASE_MENU.get(), ::CaseScreen)
MenuScreens.register(Menus.RELAY_MENU.get(), ::RelayScreen)
MenuScreens.register(Menus.RACK_MENU.get(), ::RackScreen)
MenuScreens.register(Menus.ASSEMBLER_MENU.get(), ::AssemblerScreen)
}
}

View File

@@ -0,0 +1,193 @@
package org.neoflock.neocomputers.gui.menu
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.BufferUploader
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.Tesselator
import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.blaze3d.vertex.VertexFormat
import io.netty.buffer.Unpooled
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.sounds.SoundEvents
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.RackEntity
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.gui.widget.DynamicSlot
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.utils.GenericContainerMenu
class RackSlot(container: Container, slot: Int, x: Int, y: Int) : DynamicSlot(container, slot, x, y), GuiEventListener {
// i hate that i made this, my regret is immeasurable
// i genuinely cant help myself
val MAIN_COLOURS = listOf(0xff8382d8.toInt(), 0xff75bdc1.toInt(), 0xffc8ca5f.toInt(), 0xffdb7d75.toInt(), 0xff7ec95f.toInt())
val DARK_COLOURS = listOf(0xff6a6ab0.toInt(), 0xff60999d.toInt(), 0xffa2a44e.toInt(), 0xffb36660.toInt(), 0xff67a34e.toInt())
val LIGHT_COLOURS = listOf(0xffdcdcf0.toInt(), 0xffdcdcf0.toInt(), 0xffececd4.toInt(), 0xfff0dbd9.toInt(), 0xffdbecd4.toInt())
val secondaries = 2 // TODO: make this actually change depending on how many network cards
// todo: kotlin getters and setters
fun getSelected(i: Int): Int = (container as RackEntity).conns[containerSlot*4+i]
fun setSelected(i: Int, v: Int) { (container as RackEntity).conns[containerSlot*4+i] = v }
override fun draw(graphics: GuiGraphics, mouseX: Int, mouseY: Int) {
super.draw(graphics, mouseX, mouseY)
if (!hasItem()) { drawQuad(graphics, ComponentRoles.getTextureFor("rack"), x, y, 16, 16, 0f, 0f, 15f, 15f); return; }
for (i in 0..secondaries) {
if (getSelected(i) > -1) drawConnection(graphics, getSelected(i), i-1)
}
drawEndpoints(graphics, mouseX, mouseY, secondaries)
}
override fun mayPlace(stack: ItemStack): Boolean {
if (stack.item !is ComponentItem) return false
return (stack.item as ComponentItem).getComponentRoles(stack).contains(ComponentRoles.RACK_MOUNTABLE)
}
override fun onTake(player: Player, stack: ItemStack) {
super.onTake(player, stack)
setSelected(0, -1)
setSelected(1, -1)
setSelected(2, -1)
setSelected(3, -1)
(container as RackEntity).setChanged()
}
fun drawConnection(guiGraphics: GuiGraphics, side: Int, sec: Int = -1) {
val bufferSource = guiGraphics.bufferSource()
val buffer = bufferSource.getBuffer(RenderType.gui())
if (sec == -1) {
drawColor(guiGraphics, buffer, 18, 1, DARK_COLOURS[side], 6 + (11 * side))
drawColor(guiGraphics, buffer, 18, 2, MAIN_COLOURS[side], 6 + (11 * side))
drawColor(guiGraphics, buffer, 18, 3, LIGHT_COLOURS[side], 6 + (11 * side))
} else {
drawColor(guiGraphics, buffer, 18, 6+(4*sec), MAIN_COLOURS[side], 6+(11*side))
drawColor(guiGraphics, buffer, 18, 7+(4*sec), 0xff8f8f90.toInt(), 6+(11*side))
}
}
fun drawEndpoints(guiGraphics: GuiGraphics, mx: Int, my: Int, sec: Int =0) {
val bufferSource = guiGraphics.bufferSource()
val buffer = bufferSource.getBuffer(RenderType.gui())
// main slot endpoint
drawColor(guiGraphics, buffer, 17, 1, 0xff888888.toInt())
drawColor(guiGraphics, buffer, 17, 3, 0xffffffff.toInt())
// main cable endpoints
for (i in 0..4) {
drawColor(guiGraphics, buffer, 24+(11*i), 1, 0xff333333.toInt(), 1, 3)
drawColor(guiGraphics, buffer, 25+(11*i), 1, MAIN_COLOURS[i], 3, 3)
drawColor(guiGraphics, buffer, 28+(11*i), 1, 0xffffffff.toInt(), 1, 3)
// highlight
if (mx >= x+25+(11*i) && mx <= x+27+(11*i) && my >= y+1 && my <= y+3) {
drawColor(guiGraphics, buffer, 25+(11*i), 1, 0x80FFFFFF.toInt(), 3, 3)
}
}
// secondary endpoints
for (i in 0..<sec) {
// slot
drawColor(guiGraphics, buffer, 17, 6+(4*i), 0xffffffff.toInt())
drawColor(guiGraphics, buffer, 17, 7+(4*i), 0xff888888.toInt())
// cable
for (j in 0..4) {
drawColor(guiGraphics, buffer, 24+(11*j), 6+(4*i), 0xff333333.toInt(), 1, 2)
drawColor(guiGraphics, buffer, 25+(11*j), 6+(4*i), MAIN_COLOURS[j], 3, 2)
drawColor(guiGraphics, buffer, 28+(11*j), 6+(4*i), 0xffffffff.toInt(), 1, 2)
// highlight
if (mx >= x+25+(11*j) && mx <= x+27+(11*j) && my >= y+6+(4*i) && my <= y+8+(4*i)) {
drawColor(guiGraphics, buffer, 25+(11*j), 6+(4*i), 0x80FFFFFF.toInt(), 3, 2)
}
}
}
}
fun encode(buf: FriendlyByteBuf) { // client -> server
buf.writeInt(containerSlot)
buf.writeInt(getSelected(0))
buf.writeInt(getSelected(1))
buf.writeInt(getSelected(2))
buf.writeInt(getSelected(3))
}
// TODO: replace with graphics.fill (cant be assed atm)
fun drawColor(guiGraphics: GuiGraphics, buffer: VertexConsumer, _x:Int, _y: Int, col: Int, width: Int=1, height: Int=1) {
val pose = guiGraphics.pose().last()
// x+_x+1 is one im not proud of
buffer.addVertex(pose, x+_x+width.toFloat(), y+_y+height.toFloat(), 2f).setColor(col)
buffer.addVertex(pose, x+_x+width.toFloat(), y+_y.toFloat(), 2f).setColor(col)
buffer.addVertex(pose, x+_x.toFloat(), y+_y.toFloat(), 2f).setColor(col)
buffer.addVertex(pose, x+_x.toFloat(), y+_y+height.toFloat(), 2f).setColor(col)
}
fun clickynoise() {
val handler = Minecraft.getInstance().soundManager
handler.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
for (i in 0..4) { // main line
if (mouseX >= x+25+(11*i) && mouseX <= x+28+(11*i) && mouseY >= y+1 && mouseY <= y+4 && button == 0) {
setSelected(0, if (getSelected(0) != i) i else -1)
(container as RackEntity).setChanged()
clickynoise()
return true
}
}
for (i in 0..<secondaries) { // secondary lines
for (j in 0..4) {
if (mouseX >= x+25+(11*j) && mouseX <= x+28+(11*j) && mouseY >= y+6+(4*i) && mouseY <= y+8+(4*i) && button == 0) {
setSelected(i+1, if (getSelected(i+1) != j) j else -1)
(container as RackEntity).setChanged()
clickynoise()
return true
}
}
}
return super.mouseClicked(mouseX, mouseY, button)
}
override fun setFocused(focused: Boolean) {
}
override fun isFocused(): Boolean {
return false
}
}
class RackMenu : GenericContainerMenu {
constructor(i: Int, inv: Inventory, buf: FriendlyByteBuf) : this(i, inv, (inv.player.level().getBlockEntity(buf.readBlockPos()) as RackEntity))
constructor(i: Int, inv: Inventory, container: Container) : super(Menus.RACK_MENU.get(), i, container) {
for(i in 0..3) {
val slot = RackSlot(container, i, 20, 23+i*20)
this.addSlot(slot)
}
this.addInventorySlots(inv, 8, 128)
}
}

View File

@@ -0,0 +1,85 @@
package org.neoflock.neocomputers.gui.menu
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.RelayEntity
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.gui.widget.DynamicSlot
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.item.RelayUpgrade
import org.neoflock.neocomputers.utils.GenericContainerMenu
class RelaySlot(container: Container, slot: Int, x: Int, y: Int, val role: String, val relay: RelayEntity?) : DynamicSlot(container, slot, x, y) {
override fun mayPlace(stack: ItemStack): Boolean {
if(stack.isEmpty) return true
val upgrade = stack.item as? RelayUpgrade ?: return false
if(containerSlot == RelayEntity.CARD) {
return upgrade.isRelayCompatibleCard(stack)
}
if(containerSlot == RelayEntity.CPU) {
return upgrade.getRelayInterval(stack) != null
}
if(containerSlot == RelayEntity.MEM) {
return upgrade.getRelayBufferSize(stack) != null
}
if(containerSlot == RelayEntity.STORAGE) {
return upgrade.getRelayQueueSize(stack) != null
}
return false
}
override fun set(stack: ItemStack) {
super.set(stack)
val item = stack.item
if(item is ComponentItem) {
item.whenComponentPlaced(stack, relay, role)
}
}
override fun onTake(player: Player, stack: ItemStack) {
super.onTake(player, stack)
val item = stack.item
if(item is ComponentItem) {
item.whenComponentTaken(stack, relay, role)
}
}
override fun getMaxStackSize() = 1
override fun getMaxStackSize(stack: ItemStack) = 1
override fun draw(graphics: GuiGraphics, mouseX: Int, mouseY: Int) {
super.draw(graphics, mouseX, mouseY)
if(!hasItem()) {
drawQuad(graphics, ComponentRoles.getTextureFor(role), x-1, y-1, 18, 18, 0f, 0f, 15f, 15f)
}
}
}
class RelayMenu : GenericContainerMenu {
constructor(i: Int, inv: Inventory) : this(i, inv, SimpleContainer(RelayEntity.SLOT_COUNT))
constructor(i: Int, inv: Inventory, container: Container) : super(Menus.RELAY_MENU.get(), i, container) {
val relay = container as? RelayEntity
val relayMenuWidth = 176
val itemX = 152
val itemRowDist = 16
val itemSpacing = 20
this.addSlot(RelaySlot(container, RelayEntity.CARD, relayMenuWidth+4, itemRowDist, ComponentRoles.NETWORK, relay))
this.addSlot(RelaySlot(container, RelayEntity.CPU, itemX, itemRowDist, ComponentRoles.COMPUTE, relay))
this.addSlot(RelaySlot(container, RelayEntity.MEM, itemX, itemRowDist+itemSpacing, ComponentRoles.MEMORY, relay))
this.addSlot(RelaySlot(container, RelayEntity.STORAGE, itemX, itemRowDist+itemSpacing*2, ComponentRoles.STORAGE, relay))
this.addInventorySlots(inv, 8, 84)
}
}

View File

@@ -1,5 +1,6 @@
package org.neoflock.neocomputers.gui.menu;
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.gui.menu.Menus;
@@ -7,7 +8,16 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.BlockEntity
import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.utils.GenericContainerMenu
class ScreenMenu(i: Int, inv: Inventory) : GenericContainerMenu(Menus.SCREEN_MENU.get(), i, SimpleContainer(0)) {
class ScreenMenu : GenericContainerMenu {
var entity: ScreenEntity? = null
constructor(i: Int, inv: Inventory, buf: FriendlyByteBuf) : this(i, inv, inv.player.level().getBlockEntity(buf.readBlockPos()))
constructor(i: Int, inv: Inventory, entity: BlockEntity?) : super(Menus.SCREEN_MENU.get(), i, SimpleContainer(0)) {
this.entity = entity as ScreenEntity
}
}

View File

@@ -7,26 +7,28 @@ import net.minecraft.client.renderer.texture.TextureManager
import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.utils.FontProvider
import org.neoflock.neocomputers.utils.GPUChar
import org.neoflock.neocomputers.utils.TextBuffer
import java.io.File
import kotlin.experimental.and
import kotlin.experimental.xor
class BufferRenderer(private var width: Int, private var height: Int, private var id: ResourceLocation, private var buffer: MutableList<GPUChar>) { // TODO: NN buffer
class BufferRenderer(private var id: ResourceLocation, var buffer: TextBuffer) { // TODO: NN buffer
val CHARW = 8
val CHARH = 16
private var texwidth: Int = width*CHARW;
private var texheight: Int = height*CHARH;
private var texwidth: Int = buffer.width*CHARW;
private var texheight: Int = buffer.height*CHARH;
private var image: NativeImage = NativeImage(texwidth, texheight, true); // idk what the boolean is
private var tex: DynamicTexture = DynamicTexture(image)
fun dump(path: String) {
image.writeToFile(File(path))
NeoComputers.LOGGER.info("DUMPED!!!")
init {
Minecraft.getInstance().textureManager.register(this.id, tex)
}
fun toRGBA(color: Int): Int {
return color.shl(8).or(0xFF)
// Minecaft lies, its AGBR
return java.lang.Integer.reverseBytes((color.toLong() * 256 + 0xFF).toInt())
}
fun drawGlyph(x: Int, y: Int, c: Char, fg: Int) {
@@ -42,32 +44,21 @@ class BufferRenderer(private var width: Int, private var height: Int, private va
}
fun drawBuffer() {
for (i in 0..<width) {
for (j in 0..<height) {
var char: GPUChar = get(i, j)
for (i in 0..<buffer.width) {
for (j in 0..<buffer.height) {
var char: GPUChar = buffer.get(i, j)
var x = i*CHARW
var y = j*CHARH
image.fillRect(x, y, CHARW, CHARH, (0xFF000000+char.bg).toInt())
image.fillRect(x, y, CHARW, CHARH, toRGBA(char.bg))
if (char.c != ' ' && char.c != '\u0000') drawGlyph(x, y, char.c, char.fg)
}
}
tex.upload()
}
fun get(x: Int, y: Int) = buffer[y*width+x]
fun set(x: Int, y: Int, c: GPUChar) {
buffer[y*width+x] = c
}
fun register() {
Minecraft.getInstance().textureManager.register(this.id, tex) // also idk how to unregister this
}
fun clean() {
Minecraft.getInstance().textureManager.release(this.id)
image.close()
tex.close()
}
data class GPUChar(val c: Char, val fg: Int =0xFFFFFF, val bg: Int = 0)
}

View File

@@ -0,0 +1,39 @@
package org.neoflock.neocomputers.gui.screen
import io.netty.buffer.Unpooled
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.gui.menu.AssemblerMenu
import org.neoflock.neocomputers.gui.menu.CaseMenu
import org.neoflock.neocomputers.gui.widget.ButtonSprites
import org.neoflock.neocomputers.gui.widget.ImagerButton
import org.neoflock.neocomputers.network.NodeSynchronizer
import org.neoflock.neocomputers.utils.GenericContainerScreen
class AssemblerScreen : GenericContainerScreen<AssemblerMenu> {
private var btn: ImagerButton? = null
private val PCB: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/computer.png")
private val BTN: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/button_power.png")
constructor(abstractContainerMenu: AssemblerMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
btn = ImagerButton(
8, 104,
18, 18,
ButtonSprites(BTN, 18, 18, 36, 36)
) {
// val buf = FriendlyByteBuf(Unpooled.buffer())
// buf.writeByte(if(isOn) 0x02 else 0x01)
// NodeSynchronizer.sendScreenInteraction(buf)
}
addWidget(btn!!)
}
override fun renderbg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {
guiGraphics.blit(PCB, 0, 0, 0, 0, this.imageWidth, this.imageHeight) // WE'RE FREE
}
}

View File

@@ -1,49 +1,84 @@
package org.neoflock.neocomputers.gui.screen;
import com.mojang.blaze3d.vertex.BufferBuilder
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.Tesselator
import com.mojang.blaze3d.vertex.VertexFormat
import io.netty.buffer.Unpooled
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.ImageButton
import net.minecraft.client.gui.components.WidgetSprites
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.tooltip.TooltipComponent
import net.minecraft.world.phys.Vec3
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.NodeSynchronizer
import org.neoflock.neocomputers.network.NodeSynchronizer
import org.neoflock.neocomputers.gui.menu.CaseMenu
import org.neoflock.neocomputers.gui.widget.ButtonSprites
import org.neoflock.neocomputers.gui.widget.ImagerButton
import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.utils.Formatting
import org.neoflock.neocomputers.utils.GenericContainerScreen
import java.util.Optional
class CaseScreen : GenericContainerScreen<CaseMenu> {
private val PCB: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/computer.png")
private val BTN: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/button_power.png")
// private val BTN_ENABLED: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/button/power/enabled.png") // gonna do this later
// private val BTN_DISABLED: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/button/power/disabled.png")
// private val BTN_ENABLED_HOVER: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/button/power/enabled_hover.png")
// private val BTN_DISABLED_HOVER: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/power/disabled_hover.png")
private var btn: ImagerButton? = null
override fun shouldCenterTitle(): Boolean = false
// override fun findMenuTexture(): ResourceLocation = BG
var isOn = false
var lastError: String? = null
var energy: Long = 0L
var maxEnergy: Long = 0L
var memory: Long = 0L
var maxMemory: Long = 0L
var components: Long = 0L
var maxComponents: Long = 0L
var arch = ""
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
super.processScreenStatePacket(buf)
isOn = buf.readBoolean()
btn?.pressed = isOn
val error = buf.readByteArray().decodeToString()
if(error.isEmpty()) {
lastError = null
} else {
lastError = error
}
energy = buf.readLong()
maxEnergy = buf.readLong()
memory = buf.readLong()
maxMemory = buf.readLong()
components = buf.readLong()
maxComponents = buf.readLong()
arch = buf.readUtf()
}
fun getErrorComponent(err: String): Component = if(err.startsWith("@")) Component.translatable(err.substring(1)) else Component.literal(err)
fun computeButtonTooltip(): List<Component> {
val msgs = mutableListOf(Component.translatable(if(isOn) "neocomputers.computer.on" else "neocomputers.computer.off").withStyle(if(isOn) ChatFormatting.GREEN else ChatFormatting.RED))
if(lastError != null) {
msgs.addLast(Component.translatable("neocomputers.computer.errorNoMsg").withStyle(ChatFormatting.RED).append(getErrorComponent(lastError!!)))
}
if(arch.isNotEmpty()) {
msgs.addLast(Component.translatable("neocomputers.arch", arch))
}
if(hasShiftDown()) {
msgs.addLast(Component.translatable("neocomputers.computer.energy", energy, maxEnergy).withStyle(if(energy < maxEnergy/5) ChatFormatting.RED else ChatFormatting.WHITE))
msgs.addLast(Component.translatable("neocomputers.computer.memory", Formatting.formatMemory(memory), Formatting.formatMemory(maxMemory)))
msgs.addLast(Component.translatable("neocomputers.computer.components", components, maxComponents).withStyle(if(components <= maxComponents) ChatFormatting.WHITE else ChatFormatting.RED))
}
return msgs
}
constructor(abstractContainerMenu: CaseMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
btn = ImagerButton(
15, 15,
71, 33,
18, 18,
ButtonSprites(BTN, 18, 18, 36, 36)
) {
@@ -51,27 +86,18 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
buf.writeByte(if(isOn) 0x02 else 0x01)
NodeSynchronizer.sendScreenInteraction(buf)
}
// addRenderableWidget(btn!!)
}
override fun renderBg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {
super.renderBg(guiGraphics, f, i ,j)
val relX = (this.width - this.imageWidth) / 2
val relY = (this.height - this.imageHeight) / 2
btn!!.x = relX+70
btn!!.y = relY+33
btn!!.render(guiGraphics, i, j, f) // minecraft SUCKSSS
guiGraphics.blit(PCB, relX, relY, 0, 0, this.imageWidth, this.imageHeight)
addWidget(btn!!)
}
override fun renderbg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {
guiGraphics.blit(PCB, 0, 0, 0, 0, this.imageWidth, this.imageHeight) // WE'RE FREE
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { // todo: make a better widget system than mojang, practically not even using the fact it's a widget atp
NeoComputers.LOGGER.info(String.format("btn: %d %d %d %d, mouse %s %s", btn!!.x, btn!!.y, btn!!.x+btn!!.width, btn!!.y+btn!!.height, mouseX.toString(), mouseY.toString()))
if (button != 0) return false
if (btn!!.x < mouseX.toInt() && mouseX.toInt() < btn!!.x+btn!!.width && btn!!.y < mouseY.toInt() && mouseY.toInt() < btn!!.y+btn!!.height) {
btn!!.playDownSound(Minecraft.getInstance().soundManager)
btn!!.onClick(mouseX, mouseY)
return true
} else return false
override fun renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) {
super.renderCustomOverlay(graphics, mouseX, mouseY, blend)
if(btn!!.isHovered) {
graphics.renderTooltip(this.font, computeButtonTooltip(), Optional.empty<TooltipComponent>(), mouseX, mouseY)
}
}
}

View File

@@ -0,0 +1,106 @@
package org.neoflock.neocomputers.gui.screen
import io.netty.buffer.Unpooled
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.SpriteIconButton
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.narration.NarrationElementOutput
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.RackEntity
import org.neoflock.neocomputers.gui.menu.RackMenu
import org.neoflock.neocomputers.gui.menu.RackSlot
import org.neoflock.neocomputers.gui.widget.IconTextButton
import org.neoflock.neocomputers.network.NodeSynchronizer
import org.neoflock.neocomputers.utils.GenericContainerScreen
import java.util.function.Supplier
class RackScreen(menu: RackMenu, inventory: Inventory, component: Component) : GenericContainerScreen<RackMenu>(menu, inventory, component) {
override fun findMenuTexture(): ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/rack.png")
val RELAY = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/relay.png")
var relay_mode = if (menu.container is RackEntity) (menu.container as RackEntity).relayMode else false
val relaybtn = IconTextButton(100, 96, if(relay_mode) "Enabled" else "Disabled", RELAY, width = 64) {
if (relay_mode){
it.message = Component.literal("Disabled")
relay_mode = false
} else {
it.message = Component.literal("Enabled")
relay_mode = true
}
val buffer = FriendlyByteBuf(Unpooled.buffer())
buffer.writeBoolean(relay_mode)
(menu.slots[0] as RackSlot).encode(buffer)
NodeSynchronizer.sendScreenInteraction(buffer)
}
init {
this.imageWidth = 175
this.imageHeight = 209
this.inventoryLabelY = imageHeight - 93
addWidget(relaybtn)
}
override fun renderbg(guiGraphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {
renderSideLabels(guiGraphics)
if(relay_mode) renderRelayConnections(guiGraphics)
}
fun renderRelayConnections(graphics: GuiGraphics) {
for(i in 0..3) {
val x = 50+(i*11)
graphics.fill(x, 104, x+4, 105, 0xffffffff.toInt())
graphics.fill(x, 105, x+4, 106, 0xff888888.toInt())
}
}
fun renderSideLabels(graphics: GuiGraphics) {
val x = 115+7
val y = 20
graphics.drawString(font, "Bottom", x, y, 0x404040, false)
graphics.drawString(font, "Top", x, y+11, 0x404040, false)
graphics.drawString(font, "Back", x, y+22, 0x404040, false)
graphics.drawString(font, "Right", x, y+33, 0x404040, false)
graphics.drawString(font, "Left", x, y+44, 0x404040, false)
}
// override fun processScreenStatePacket(buf: FriendlyByteBuf) {
// super.processScreenStatePacket(buf)
//// NeoComputers.LOGGER.info("porcessing screen state packet...")
//// relay_mode = buf.readBoolean()
//// if (relay_mode) relaybtn.message = Component.literal("Enabled")
//// else relaybtn.message = Component.literal("Disabled")
////
//// for (slot in menu.slots) {
//// if (slot is RackSlot) {
//// slot.processStateScreenPacket(buf)
//// }
//// }
//
// }
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (super.mouseClicked(mouseX, mouseY, button)) return true
for (slot in menu.slots) {
if (slot is RackSlot) {
if(slot.mouseClicked(mouseX-imageX, mouseY-imageY, button)) {
val buf = FriendlyByteBuf(Unpooled.buffer())
buf.writeBoolean(relay_mode)
slot.encode(buf)
NodeSynchronizer.sendScreenInteraction(buf)
return true
}
}
}
return false;
}
}

View File

@@ -0,0 +1,51 @@
package org.neoflock.neocomputers.gui.screen
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.Style
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.DyeColor
import org.neoflock.neocomputers.gui.menu.RelayMenu
import org.neoflock.neocomputers.utils.GenericContainerScreen
import kotlin.math.max
import kotlin.math.min
class RelayScreen(abstractContainerMenu: RelayMenu, inventory: Inventory, component: Component): GenericContainerScreen<RelayMenu>(abstractContainerMenu, inventory, component) {
var interval: Int = 5
var bufferSize: Int = 1
var queueSize: Int = 20
var inQueue: Int = 0
override fun shouldCenterTitle(): Boolean = false
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
super.processScreenStatePacket(buf)
interval = buf.readVarInt()
bufferSize = buf.readVarInt()
queueSize = buf.readVarInt()
inQueue = buf.readVarInt()
}
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
super.render(graphics, mouseX, mouseY, something)
val textX = imageX + 12
val dataX = imageX + 100
val textSpacing = 20
val textY = imageY + 20
val clr = ChatFormatting.DARK_GRAY.color!!
graphics.drawString(this.font, "Cycle rate", textX, textY, clr, false)
graphics.drawString(this.font, "Packets / cycle", textX, textY + textSpacing, clr, false)
graphics.drawString(this.font, "Queue Size", textX, textY + textSpacing*2, clr, false)
val hz = if(interval == 0) 20 else 20 / interval
val buffered = min(inQueue, bufferSize)
val queued = max(inQueue - bufferSize, 0)
graphics.drawString(this.font, "$hz Hz", dataX, textY, clr, false)
graphics.drawString(this.font, "$buffered / $bufferSize", dataX, textY + textSpacing, clr, false)
graphics.drawString(this.font, "$queued / $queueSize", dataX, textY + textSpacing * 2, clr, false)
}
}

View File

@@ -6,37 +6,50 @@ import com.mojang.blaze3d.vertex.Tesselator
import com.mojang.blaze3d.vertex.VertexFormat
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.gui.buffer.BufferRenderer
import org.neoflock.neocomputers.gui.menu.ScreenMenu
import org.neoflock.neocomputers.gui.render.ScreenRenderer
import org.neoflock.neocomputers.utils.GenericContainerScreen
import org.neoflock.neocomputers.utils.TextBuffer
import kotlin.math.min
class ScreenScreen : AbstractContainerScreen<ScreenMenu>{
class ScreenScreen : GenericContainerScreen<ScreenMenu>{
private var renderer: ScreenRenderer = ScreenRenderer();
private var bufferRenderer: BufferRenderer? = null;
constructor(abstractContainerMenu: ScreenMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
val buffer: ArrayList<BufferRenderer.GPUChar> = arrayListOf(BufferRenderer.GPUChar('h'), BufferRenderer.GPUChar('a'), BufferRenderer.GPUChar('i'))
for (i in 0..<(400-3)) {
buffer.add(BufferRenderer.GPUChar(' '))
}
bufferRenderer = BufferRenderer(20, 20, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen/test"), buffer)
bufferRenderer!!.register()
bufferRenderer!!.drawBuffer()
renderer.bind(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen/test"))
}
override fun renderBg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {}
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
super.render(graphics, mouseX, mouseY, something)
renderer.render(graphics, 50, 50, 100, 200)
var textBuf = TextBuffer(0, 0)
override fun shouldCenterTitle(): Boolean = false
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
super.processScreenStatePacket(buf)
textBuf.decodeContents(buf)
}
override fun onClose() {
super.onClose()
bufferRenderer!!.clean()
constructor(abstractContainerMenu: ScreenMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
var ent: ScreenEntity = abstractContainerMenu.entity!!;
renderer.bind(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, ent.bound))
// advanced graphics programming
this.titleLabelX = Int.MAX_VALUE
this.inventoryLabelX = Int.MAX_VALUE
}
override fun renderBg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {
if(textBuf.width > 0) {
imageWidth = textBuf.width * 4
imageHeight = textBuf.height * 8
renderer.render(guiGraphics, imageX, imageY, imageWidth, imageHeight)
}
}
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) { }
// override fun onClose() {
// super.onClose()
// renderer.
// }
}

View File

@@ -6,11 +6,9 @@ import net.minecraft.client.renderer.GameRenderer
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.ItemLike
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.item.ComponentItem
// Sort of a mis-nomer, does not need to be associated with components specifically
@@ -85,14 +83,11 @@ data class ComponentSlotRequirement(val tier: Int, val role: String) {
// Tier 0 allows ALL tiers, making it completely untiered.
// Role determines what the role is.
class ComponentSlot(container: Container, slot: Int, x: Int, y: Int, val machine: MachineEntity?, val requirement: ComponentSlotRequirement): DynamicSlot(container, slot, x, y) {
override fun draw(graphics: GuiGraphics, relX: Int, relY: Int, mouseX: Int, mouseY: Int) {
super.draw(graphics, relX, relY, mouseX, mouseY)
class ComponentSlot(container: Container, slot: Int, x: Int, y: Int, val machine: ComponentUser?, var requirement: ComponentSlotRequirement): DynamicSlot(container, slot, x, y) {
override fun draw(graphics: GuiGraphics, mouseX: Int, mouseY: Int) {
super.draw(graphics, mouseX, mouseY)
if(!hasItem()) {
RenderSystem.enableBlend()
RenderSystem.setShaderTexture(0, ComponentRoles.getTextureFor(requirement.role))
RenderSystem.setShader { GameRenderer.getPositionTexShader() }
drawQuad(relX + x - 1, relY + y - 1, 18, 18, 0F, 0F, 15F, 15F)
drawQuad(graphics, ComponentRoles.getTextureFor(requirement.role), x - 1, y - 1, 18, 18, 0F, 0F, 15F, 15F)
if (requirement.tier > 0) {
RenderSystem.setShaderTexture(
0,
@@ -101,10 +96,11 @@ class ComponentSlot(container: Container, slot: Int, x: Int, y: Int, val machine
"textures/gui/slots/tier${requirement.tier - 1}.png"
)
)
RenderSystem.setShader { GameRenderer.getPositionTexShader() }
drawQuad(relX + x - 1, relY + y - 1, 18, 18, 0F, 0F, 15F, 15F)
val tex = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/slots/tier${requirement.tier - 1}.png")
// RenderSystem.setShader { GameRenderer.getPositionTexShader() }
drawQuad(graphics, tex, x - 1, y - 1, 18, 18, 0F, 0F, 15F, 15F)
}
RenderSystem.disableBlend()
// RenderSystem.disableBlend()
}
}

View File

@@ -5,41 +5,43 @@ import com.mojang.blaze3d.vertex.BufferBuilder
import com.mojang.blaze3d.vertex.BufferUploader
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.Tesselator
import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.blaze3d.vertex.VertexFormat
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderStateShard.ShaderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.Container
import net.minecraft.world.inventory.Slot
import org.neoflock.neocomputers.NeoComputers
open class DynamicSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
open class DynamicSlot(container: Container, slot: Int, x: Int, y: Int, var active: Boolean = true) : Slot(container, slot, x, y) {
val BACKGROUND: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/slots/slot.png")
open fun draw(graphics: GuiGraphics, relX: Int, relY: Int, mouseX: Int, mouseY: Int) {
RenderSystem.enableBlend() // background
RenderSystem.setShaderTexture(0, BACKGROUND)
RenderSystem.setShader { GameRenderer.getPositionTexShader() }
drawQuad(relX+x-1, relY+y-1, 18, 18, 0F, 0F, 15F, 15F)
RenderSystem.disableBlend()
val RENDER_TYPE = { r: ResourceLocation ->
RenderType.create("nc_gui_slot", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.QUADS, RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder()
.setShaderState(ShaderStateShard.POSITION_TEX_SHADER)
.setTextureState(RenderStateShard.TextureStateShard(r, false, false))
.setTransparencyState(RenderStateShard.TransparencyStateShard.TRANSLUCENT_TRANSPARENCY)
.createCompositeState(false))
}
fun drawQuad(x: Int, y: Int, width: Int, height: Int, u1: Float, v1: Float, u2: Float, v2: Float) {
var t: Tesselator = Tesselator.getInstance()
var builder: BufferBuilder = t.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX)
builder.addVertex(x.toFloat(), (y+height).toFloat(), 1f).setUv(u1/15F, v2/15F)
builder.addVertex((x+width).toFloat(), (y+height).toFloat(), 1f).setUv(u2/15F, v2/15F)
builder.addVertex((x+width).toFloat(), y.toFloat(), 1f).setUv(u2/15F, v1/15F)
builder.addVertex(x.toFloat(), y.toFloat(), 1f).setUv(u1/15F,v1/15F)
BufferUploader.drawWithShader(builder.build()!!)
open fun draw(graphics: GuiGraphics, mouseX: Int, mouseY: Int) {
drawQuad(graphics, BACKGROUND, x-1, y-1, 18, 18, 0F, 0F, 15F, 15F)
}
// private fun renderSlotHighlight(guiGraphics: GuiGraphics, x: Int, y: Int, k: Int) { // im not sure but i tihnk i copied this from mc source code
// guiGraphics.fillGradient(RenderType.guiOverlay(), x, y, x + 16, y + 16, -2130706433, -2130706433, k);
// }
fun drawQuad(guiGraphics: GuiGraphics, tex: ResourceLocation, x: Int, y: Int, width: Int, height: Int, u1: Float, v1: Float, u2: Float, v2: Float) {
val pose = guiGraphics.pose().last()
val builder = guiGraphics.bufferSource().getBuffer(RENDER_TYPE(tex))
builder.addVertex(pose, x.toFloat(), (y+height).toFloat(), 1f).setUv(u1/15F, v2/15F)
builder.addVertex(pose, (x+width).toFloat(), (y+height).toFloat(), 1f).setUv(u2/15F, v2/15F)
builder.addVertex(pose, (x+width).toFloat(), y.toFloat(), 1f).setUv(u2/15F, v1/15F)
builder.addVertex(pose, x.toFloat(), y.toFloat(), 1f).setUv(u1/15F,v1/15F)
}
override fun isActive() = active
}

View File

@@ -0,0 +1,49 @@
package org.neoflock.neocomputers.gui.widget
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.renderer.texture.TextureAtlas
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.ResourceLocationPattern
import org.neoflock.neocomputers.NeoComputers
import java.util.function.Supplier
class press(val lambda: (IconTextButton) -> Unit) : Button.OnPress {
override fun onPress(button: Button) {
lambda(button as IconTextButton)
}
}
class narr : Button.CreateNarration {
override fun createNarrationMessage(supplier: Supplier<MutableComponent?>): MutableComponent? {
return supplier.get() // no narration for u
}
}
class IconTextButton(x: Int, y: Int, text: String, val icon: ResourceLocation, val iconw: Int =16, val iconh: Int = 16, width: Int=150, height: Int=20, lambda: (IconTextButton) -> Unit) :
Button(x, y, width, height, Component.literal(text),press(lambda), narr()) {
var xOffset = 2
override fun renderWidget(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
super.renderWidget(guiGraphics, mouseX, mouseY, partialTick)
RenderSystem.disableBlend() // i hate this
val imx = x + xOffset
val imy = y + (height/2) - (iconh/2)
guiGraphics.blit(icon, imx, imy, 0f, 0f, iconw, iconh, iconw, iconh)
RenderSystem.enableBlend()
}
override fun renderString(guiGraphics: GuiGraphics, font: Font, color: Int) {
val startx = x + iconw/2 // idk why /2, might be coincidence thing
renderScrollingString(guiGraphics, font, message, startx, y, startx+width, y+height, color)
}
}

View File

@@ -2,18 +2,17 @@ package org.neoflock.neocomputers.item
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
open class CBUSItem(val tier: Int, val maxComponents: Int): Item(Item.Properties()), ComponentItem {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.BUS)
override fun getComponentRoles(itemStack: ItemStack) = setOf(ComponentRoles.BUS)
override fun getComponentTier(itemStack: ItemStack): Int = tier
override fun getComponentTier(itemStack: ItemStack) = tier
override fun getComponentCapacity(itemStack: ItemStack): Int = maxComponents
override fun getComponentCapacity(itemStack: ItemStack) = maxComponents
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
}
class CBUS0: CBUSItem(1, 8)
class CBUS1: CBUSItem(2, 12)

View File

@@ -2,18 +2,21 @@ package org.neoflock.neocomputers.item
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
open class CPUItem(val tier: Int, val maxComponents: Int): Item(Item.Properties()), ComponentItem {
open class CPUItem(val tier: Int, val maxComponents: Int): Item(Item.Properties()), RelayUpgrade {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.COMPUTE)
override fun getComponentTier(itemStack: ItemStack): Int = tier
override fun getComponentCapacity(itemStack: ItemStack): Int = maxComponents
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf("Lua 5.3")
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun getRelayInterval(itemStack: ItemStack) = 4 / tier
}
class CPU0: CPUItem(1, 8)

View File

@@ -1,8 +1,10 @@
package org.neoflock.neocomputers.item
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.MachineEvent
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking
import java.util.UUID
@@ -14,28 +16,29 @@ interface ComponentItem {
// Get machine properties they can influence
fun getMemoryCapacity(itemStack: ItemStack): Int = 0
fun getComponentCapacity(itemStack: ItemStack): Int = 0
fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf()
// Component placed, node must now exist
fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
val oldNode = getComponentNode(itemStack)
if(oldNode != null) Networking.removeNode(oldNode) // did a mod forget to call whenComponentTaken?
val node = toComponentNode(itemStack, machine) ?: return
Networking.addNode(node)
machine.getMachineNode().connectTo(node)
machine?.getMachineNode()?.connectTo(node)
}
// Component taken, and thus removed
fun whenComponentTaken(itemStack: ItemStack, machine: MachineEntity, previousRole: String) {
fun whenComponentTaken(itemStack: ItemStack, machine: ComponentUser?, previousRole: String) {
val node = getComponentNode(itemStack) ?: return
node.disconnectFrom(machine.getMachineNode())
// removing disconnects
Networking.removeNode(node)
}
// To node, if applicable. Meant to create the node, but not add it, as it will use the itemStack's address to find it again
fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node?
fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?): DeviceNode?
// Gets the node associated to an item, if it exists
fun getComponentNode(itemStack: ItemStack): Networking.Node? {
fun getComponentNode(itemStack: ItemStack): DeviceNode? {
val address = itemStack.get(DataComponents.ADDRESS) ?: return null
val uuid = UUID.fromString(address) ?: return null
return Networking.getNode(uuid)
@@ -49,4 +52,12 @@ interface ComponentItem {
}
fun onMachineEvent(itemStack: ItemStack, machine: MachineEntity, event: MachineEvent) {}
}
// A special ComponentItem which specifies upgrades specific to the relay
interface RelayUpgrade: ComponentItem {
fun getRelayInterval(itemStack: ItemStack): Int? = null
fun getRelayBufferSize(itemStack: ItemStack): Int? = null
fun getRelayQueueSize(itemStack: ItemStack): Int? = null
fun isRelayCompatibleCard(itemStack: ItemStack): Boolean = false
}

View File

@@ -4,9 +4,8 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting
// Note: We'll prob want to replace them with NN component configs later on
@@ -16,13 +15,13 @@ open class DataCard(val tier: Int, val limit: Long): Item(Properties()), Compone
override fun getComponentTier(itemStack: ItemStack): Int = tier
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
// TODO: Modem Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

View File

@@ -15,8 +15,18 @@ object DataComponents {
DataComponentType.builder<String>().persistent(Codec.STRING).build())
val READONLY = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "readonly"),
DataComponentType.builder<Boolean>().persistent(Codec.BOOL).build())
val ARCH = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "architecture"),
DataComponentType.builder<String>().persistent(Codec.STRING).build())
val EEPROM_CODE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_code"),
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
val EEPROM_DATA = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_data"),
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
val EEPROM_CODESIZE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_codesize"),
DataComponentType.builder<Int>().persistent(Codec.INT).build())
val EEPROM_DATASIZE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_datasize"),
DataComponentType.builder<Int>().persistent(Codec.INT).build())
val TUNNEL_CHANNEL = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "tunnel_channel"),
DataComponentType.builder<String>().persistent(Codec.STRING).build())
}

View File

@@ -4,15 +4,16 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting
import java.nio.ByteBuffer
fun getEEPROMProperties(codeCap: Int, dataCap: Int): Item.Properties = Item.Properties()
.component(DataComponents.EEPROM_CODE, ByteBuffer.allocate(codeCap))
.component(DataComponents.EEPROM_DATA, ByteBuffer.allocate(dataCap))
.component(DataComponents.EEPROM_CODESIZE, 0)
.component(DataComponents.EEPROM_DATASIZE, 0)
.component(DataComponents.LABEL, "")
.component(DataComponents.READONLY, false)
@@ -25,12 +26,12 @@ open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: In
override fun getComponentCapacity(itemStack: ItemStack): Int = 0
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,
@@ -39,12 +40,16 @@ open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: In
tooltipFlag: TooltipFlag
) {
if(tooltipFlag.isAdvanced) {
val codeSize = itemStack.get(DataComponents.EEPROM_CODE)?.position() ?: 0
val dataSize = itemStack.get(DataComponents.EEPROM_DATA)?.position() ?: 0
val codeSize = itemStack.get(DataComponents.EEPROM_CODESIZE) ?: 0
val dataSize = itemStack.get(DataComponents.EEPROM_DATASIZE) ?: 0
val arch = itemStack.get(DataComponents.ARCH)
val addr = itemStack.get(DataComponents.ADDRESS)
val readonly = itemStack.get(DataComponents.READONLY) ?: false
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
list.addLast(addrComp)
if(arch != null) {
list.addLast(Component.translatable("neocomputers.arch", arch))
}
list.addLast(Component.translatable("neocomputers.eeprom.codeused", Formatting.formatMemory(codeSize.toLong()),
Formatting.formatMemory(codeCapacity.toLong())))
list.addLast(Component.translatable("neocomputers.eeprom.dataused", Formatting.formatMemory(dataSize.toLong()),

View File

@@ -4,9 +4,8 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
// Note: We'll prob want to replace them with NN component configs later on
@@ -15,13 +14,13 @@ open class GPUCard(val tier: Int, val vram: Long): Item(Properties()), Component
override fun getComponentTier(itemStack: ItemStack): Int = tier
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
// TODO: GPU Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

View File

@@ -4,17 +4,15 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting
import java.util.UUID
fun getDiskProperties(): Item.Properties = Item.Properties()
.component(DataComponents.LABEL, "")
.component(DataComponents.READONLY, false)
open class HardDiskItem(val tier: Int, val capacity: Long): Item(getDiskProperties()), ComponentItem {
open class HardDiskItem(val tier: Int, val capacity: Long, val relayQueueSize: Int): Item(getDiskProperties()), RelayUpgrade {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.STORAGE)
override fun getComponentTier(itemStack: ItemStack): Int = tier
@@ -23,12 +21,12 @@ open class HardDiskItem(val tier: Int, val capacity: Long): Item(getDiskProperti
override fun getComponentCapacity(itemStack: ItemStack): Int = 0
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,
@@ -56,8 +54,10 @@ open class HardDiskItem(val tier: Int, val capacity: Long): Item(getDiskProperti
}
return super.getName(itemStack)
}
override fun getRelayQueueSize(itemStack: ItemStack) = relayQueueSize
}
class HardDisk0: HardDiskItem(1, 1 shl 20)
class HardDisk1: HardDiskItem(2, 2 shl 20)
class HardDisk2: HardDiskItem(3, 4 shl 20)
class HardDisk0: HardDiskItem(1, 1 shl 20, 30)
class HardDisk1: HardDiskItem(2, 2 shl 20, 40)
class HardDisk2: HardDiskItem(3, 4 shl 20, 50)

View File

@@ -4,23 +4,21 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting
class InternetCard: Item(Item.Properties()), ComponentItem {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD, ComponentRoles.INET)
override fun getComponentTier(itemStack: ItemStack): Int = 1
override fun getComponentTier(itemStack: ItemStack): Int = 2
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
// TODO: Internet Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

View File

@@ -46,4 +46,6 @@ object Items {
val REDIO0 = ITEMS.register("redio0") { RedstoneCard0() }
val REDIO1 = ITEMS.register("redio1") { RedstoneCard1() }
val SERVER0 = ITEMS.register("server0") { ServerItem() }
}

View File

@@ -4,12 +4,11 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting
open class MemoryItem(val tier: Int, val capacity: Int): Item(Item.Properties().`arch$tab`(Tabs.TAB)), ComponentItem {
open class MemoryItem(val tier: Int, val capacity: Int, val relayBuf: Int): Item(Item.Properties().`arch$tab`(Tabs.TAB)), RelayUpgrade {
override fun getComponentRoles(itemStack: ItemStack) = setOf(ComponentRoles.MEMORY)
override fun getComponentTier(itemStack: ItemStack): Int = tier
@@ -19,7 +18,7 @@ open class MemoryItem(val tier: Int, val capacity: Int): Item(Item.Properties().
override fun getComponentCapacity(itemStack: ItemStack): Int = 0
// no node for memory
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,
@@ -32,11 +31,13 @@ open class MemoryItem(val tier: Int, val capacity: Int): Item(Item.Properties().
}
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
}
override fun getRelayBufferSize(itemStack: ItemStack) = relayBuf
}
class MemoryTier1(): MemoryItem(1, 192 shl 10)
class MemoryTier1_5(): MemoryItem(1, 256 shl 10)
class MemoryTier2(): MemoryItem(2, 384 shl 10)
class MemoryTier2_5(): MemoryItem(2, 512 shl 10)
class MemoryTier3(): MemoryItem(3, 768 shl 10)
class MemoryTier3_5(): MemoryItem(3, 1 shl 20)
class MemoryTier1(): MemoryItem(1, 192 shl 10, 2)
class MemoryTier1_5(): MemoryItem(1, 256 shl 10, 3)
class MemoryTier2(): MemoryItem(2, 384 shl 10, 4)
class MemoryTier2_5(): MemoryItem(2, 512 shl 10, 5)
class MemoryTier3(): MemoryItem(3, 768 shl 10, 6)
class MemoryTier3_5(): MemoryItem(3, 1 shl 20, 7)

View File

@@ -4,22 +4,21 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
open class NetworkCard(val tier: Int, val maxRange: Int, val isWired: Boolean): Item(Properties()), ComponentItem {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD, ComponentRoles.NETWORK)
override fun getComponentTier(itemStack: ItemStack): Int = tier
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
// TODO: Modem Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

View File

@@ -0,0 +1,62 @@
package org.neoflock.neocomputers.item
import com.mojang.blaze3d.shaders.Shader
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer
import com.mojang.blaze3d.vertex.VertexFormat
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.NeoComputers
interface RackItem {
// companion object {
// val RENDER_TYPE = {l: ResourceLocation ->
// RenderType.create("nc_server", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, RenderType.SMALL_BUFFER_SIZE, RenderType.CompositeState.builder()
// .setShaderState(RenderStateShard.ShaderStateShard.POSITION_COLOR_TEX_LIGHTMAP_SHADER)
// .setLightmapState(RenderStateShard.LIGHTMAP)
// .setTextureState(RenderStateShard.TextureStateShard(l, false, false))
// .createCompositeState(false))
// }
// }
val TOP_TEX: ResourceLocation
get() = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/block/generic_top.png")
val FRONT_TEX: ResourceLocation
get() = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/block/rack_server.png")
fun render_lights(source: MultiBufferSource, stack: PoseStack, light: Int)
fun render(source: MultiBufferSource, stack: PoseStack, light: Int, v_offset: Float = 2f) {
val pose = stack.last()
// var buffer = source.getBuffer(RenderType.gui()) // TODO: correct rendertype
var buffer = source.getBuffer(RenderType.entitySolid(TOP_TEX))
// val u1 = 1/16f
buffer.addVertex(pose, 0f, 0f, 0f).setUv(1/16f, 15/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, -1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 14/16f, 0f, 0f).setUv(1/16f, 1/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, -1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 14/16f, 0f, 14/16f).setUv(15/16f, 1/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, -1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 0f, 0f, 14/16f).setUv(15/16f, 1/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, -1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 0f, 3/16f, 14/16f).setUv(15/16f, 1/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, 1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 14/16f, 3/16f, 14/16f).setUv(15/16f, 15/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, 1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 14/16f, 3/16f, 0f).setUv(1/16f, 15/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, 1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 0f, 3/16f, 0f).setUv(1/16f, 1/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 0f, 1f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer = source.getBuffer(RenderType.entitySolid(FRONT_TEX))
buffer.addVertex(pose, 14/16f, 3/16f, 14/16f).setUv(1/16f, v_offset/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 1f, 0f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 14/16f, 0f, 14/16f).setUv(1/16f, (v_offset+3)/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 1f, 0f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 14/16f, 0/16f, 0/16f).setUv(15/16f, (v_offset+3)/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 1f, 0f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
buffer.addVertex(pose, 14/16f, 3/16f, 0/16f).setUv(15/16f, v_offset/16f).setColor(1f, 1f, 1f, 1f).setLight(light).setNormal(pose, 1f, 0f, 0f).setOverlay(OverlayTexture.NO_OVERLAY)
stack.pushPose()
stack.translate((14/16f)+0.001F, 0f, 0f)
render_lights(source, stack, light)
stack.popPose()
}
}

View File

@@ -4,9 +4,8 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
// Note: We'll prob want to replace them with NN component configs later on
@@ -15,13 +14,13 @@ open class RedstoneCard(val tier: Int): Item(Properties()), ComponentItem {
override fun getComponentTier(itemStack: ItemStack): Int = tier
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
// TODO: Redstone Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

View File

@@ -0,0 +1,27 @@
package org.neoflock.neocomputers.item
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.client.renderer.item.ItemProperties
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.DeviceNode
class ServerItem() : Item(Properties()), ComponentItem, RackItem {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.RACK_MOUNTABLE)
override fun getComponentTier(itemStack: ItemStack): Int = 0
override fun toComponentNode(
itemStack: ItemStack,
machine: ComponentUser?
): DeviceNode? {
return null // TODO: atom machine item plz
}
override fun render_lights(source: MultiBufferSource, stack: PoseStack, light: Int) {
}
}

View File

@@ -23,38 +23,9 @@ object Tabs {
ItemStack(Items.MEM0.get())
}
builder.displayItems { parameters, output ->
// TODO: get rid of arch$tab and this shi and replace with loop over items registry
output.accept(ItemStack(Items.CPU0.get()))
output.accept(ItemStack(Items.CPU1.get()))
output.accept(ItemStack(Items.CPU2.get()))
output.accept(ItemStack(Items.CBUS0.get()))
output.accept(ItemStack(Items.CBUS1.get()))
output.accept(ItemStack(Items.CBUS2.get()))
output.accept(ItemStack(Items.CBUS_CREATIVE.get()))
output.accept(ItemStack(Items.DATA0.get()))
output.accept(ItemStack(Items.DATA1.get()))
output.accept(ItemStack(Items.DATA2.get()))
output.accept(ItemStack(Items.GPU0.get()))
output.accept(ItemStack(Items.GPU1.get()))
output.accept(ItemStack(Items.GPU2.get()))
output.accept(ItemStack(Items.HDD0.get()))
output.accept(ItemStack(Items.HDD1.get()))
output.accept(ItemStack(Items.HDD2.get()))
output.accept(ItemStack(Items.INET.get()))
output.accept(ItemStack(Items.TUNNEL.get()))
output.accept(ItemStack(Items.LAN.get()))
output.accept(ItemStack(Items.WLAN0.get()))
output.accept(ItemStack(Items.WLAN1.get()))
output.accept(ItemStack(Items.REDIO0.get()))
output.accept(ItemStack(Items.REDIO1.get()))
output.accept(ItemStack(Items.EE0.get()))
Items.ITEMS.forEach {
output.accept(ItemStack(it.get()))
}
// Criminal black magic to put LuaBIOS EEPROM in the tabs
do {
@@ -71,7 +42,9 @@ object Tabs {
val codeBuf = ByteBuffer.allocate(code.size)
codeBuf.put(code)
luaBios.set(DataComponents.LABEL, "Lua BIOS")
luaBios.set(DataComponents.ARCH, "Lua 5.2")
luaBios.set(DataComponents.EEPROM_CODE, codeBuf)
luaBios.set(DataComponents.EEPROM_CODESIZE, code.size)
output.accept(luaBios)
} while(false)
}

View File

@@ -4,24 +4,22 @@ import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.ComponentUser
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting
class TunnelCard: Item(Properties()), ComponentItem {
class TunnelCard: Item(Properties().component(DataComponents.TUNNEL_CHANNEL, "creative")), ComponentItem {
// yes, we're counting TUNNEL as a conventional networking card
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD, ComponentRoles.NETWORK)
override fun getComponentTier(itemStack: ItemStack): Int = 3
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity, newRole: String) {
ensureHasAddress(itemStack)
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
if(machine != null) ensureHasAddress(itemStack)
super.whenComponentPlaced(itemStack, machine, newRole)
}
// TODO: Tunnel Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,
@@ -33,6 +31,7 @@ class TunnelCard: Item(Properties()), ComponentItem {
val addr = itemStack.get(DataComponents.ADDRESS)
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
list.addLast(addrComp)
list.addLast(Component.translatable("neocomputers.tunnel.channel", itemStack.get(DataComponents.TUNNEL_CHANNEL) ?: "creative"))
// TODO: show max packet size and whatnot
}
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)

View File

@@ -0,0 +1,256 @@
package org.neoflock.neocomputers.network
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level
import net.minecraft.world.phys.Vec3
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.network.Networking.Message
import org.neoflock.neocomputers.network.Networking.Visibility
import org.neoflock.neocomputers.network.Networking.maxHopCount
import java.util.UUID
import kotlin.math.min
// tmp class until JNI bindings work
data class NNComponent(val type: String)
open class DeviceNode(_address: UUID? = null) {
val connections = HashSet<DeviceNode>()
private var reachableCache: Set<DeviceNode>? = null
var address = _address ?: UUID.randomUUID()
open var reachability = Visibility.NETWORK
open var powerRole = PowerRole.CONSUMER
open var energy: Long = 0
open var energyCapacity: Long = 0
// give energy, returns how much was actually given
// cannot exceed amount specified
open fun giveEnergy(amount: Long): Long {
val maximum = min(amount, energyCapacity - energy)
energy += maximum
markChanged()
return maximum
}
// take energy out, returns how much was actually taken
// cannot exceed amount specified
open fun withdrawEnergy(amount: Long): Long {
val maximum = min(amount, energy)
energy -= maximum
markChanged()
return maximum
}
fun getChargerNodes(): Set<DeviceNode> = getReachable().filter { it.powerRole != PowerRole.CONSUMER }.toSet()
fun totalEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energy }
fun maxEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energyCapacity }
// attempts to consume
fun consumeEnergy(energy: Long): Boolean {
// consumes energy, returns false if not enough
val total = totalEnergyInConnections() + this.energy
if(energy > total) return false
var remaining = energy
remaining -= withdrawEnergy(remaining)
if(remaining <= 0) return true
for (charger in getChargerNodes()) {
if(remaining <= 0) break
remaining -= charger.withdrawEnergy(remaining)
}
return true
}
// PLEASE only call if consumer, in the name of all that is holy
fun tryToChargeFully() {
var remaining = energyCapacity - energy
if(remaining <= 0) return
for (charger in getChargerNodes()) {
if(remaining <= 0) break
val amount = charger.withdrawEnergy(remaining)
val given = giveEnergy(amount)
remaining -= given
if(given < amount) {
val delta = amount - given // amount lost while given back
if(charger.giveEnergy(delta) < delta) {
NeoComputers.LOGGER.warn("LOSING ENERGY! Tried giving $delta back to $charger and we're losing our marbles!")
}
}
}
}
// only call if storage
fun balanceStorage() {
for(battery in getReachable()) {
if(battery.powerRole != PowerRole.STORAGE) continue
// its so if for example we have a battery with 2x the capacity
// we don't try to even the energy between them since that's just bad
// and might pointless delete energy over time
val capacityRatio = energyCapacity.toDouble() / battery.energyCapacity
val meaningfulSurplus = (battery.energy * capacityRatio - energy).toLong()
if(meaningfulSurplus <= 0) {
// WE'RE greedy (or negligible surplus)? Do nothing
continue
}
// steal from this greedy mf
val toSteal = meaningfulSurplus / 2
if(toSteal == 0L) continue // broke storahh
val stolen = battery.withdrawEnergy(toSteal)
if(giveEnergy(stolen) < stolen) {
NeoComputers.LOGGER.warn("LOSING ENERGY IN NODE $address!!!! THIS IS REALLY BAD!!!")
}
}
}
// rob the generators
fun stealGeneratorPower() {
var remaining = energyCapacity - energy
for(generator in getReachable()) {
if(generator.powerRole != PowerRole.GENERATOR) continue
// rob this mf
val robbed = generator.withdrawEnergy(remaining)
val taken = giveEnergy(robbed)
if(taken < robbed) {
NeoComputers.LOGGER.warn("energy caught being DELETED in the big 26")
}
remaining -= taken
}
}
open fun tick() {
if(powerRole == PowerRole.CONSUMER) tryToChargeFully()
if(powerRole == PowerRole.STORAGE) {
stealGeneratorPower()
balanceStorage()
}
}
// processes a received message
open fun received(message: Message) {}
// called when a new direct connection is made
open fun onConnect(deviceNode: DeviceNode) {}
// called when a direct connection is lost
open fun onDisconnect(deviceNode: DeviceNode) {}
// called when a new node is added globally
open fun onNodeAdded(deviceNode: DeviceNode) {
invalidateReachableCache()
}
// called when a node is removed globally
open fun onNodeRemoved(deviceNode: DeviceNode) {
invalidateReachableCache()
}
fun getReachable(): Set<DeviceNode> {
if(reachableCache == null) {
reachableCache = computeReachable()
}
return reachableCache!!
}
open fun invalidateReachableCache() {
reachableCache = null
}
// Returns a subset of connections, for a subset of direct
// meant for things like drives which dont want to accidentally fuse networks
open fun getPreferredFew() = setOf<DeviceNode>()
fun computeReachable(): Set<DeviceNode> {
if(reachability == Visibility.NONE) {
return setOf()
}
if(reachability == Visibility.SOME) {
return getPreferredFew()
}
if(reachability == Visibility.NETWORK) {
// absolute cinema
val working = HashSet<DeviceNode>()
val pending = mutableListOf(this)
var iterCount = 0
while(iterCount < maxHopCount && pending.isNotEmpty()) {
iterCount++
val subnode = pending.removeFirst()
if(subnode in working) continue
working.add(subnode)
if(subnode.reachability == Visibility.NETWORK) {
pending.addAll(subnode.connections)
} else if(subnode.reachability == Visibility.SOME) {
pending.addAll(subnode.getPreferredFew())
}
}
// cannot send to itself!
working.remove(this)
return working
}
throw NotImplementedError("visibility not implemented")
}
fun connectTo(other: DeviceNode) {
this.directConnectTo(other)
other.directConnectTo(this)
}
fun disconnectFrom(other: DeviceNode) {
this.directDisconnectFrom(other)
other.directDisconnectFrom(this)
}
fun directConnectTo(other: DeviceNode) {
if(other == this) return
if(other in connections) return
connections.add(other)
this.onConnect(other)
invalidateReachableCache()
}
fun directDisconnectFrom(other: DeviceNode) {
if(other !in connections) return
connections.remove(other)
this.onDisconnect(other)
invalidateReachableCache()
}
// Network synchronization with the NodeSynchronizer
// TODO: process shi
var outOfSync = false
fun markChanged() {
outOfSync = true
}
open fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {}
open fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) {}
// Meant to write the entire state as a single commit
// for clients which say they have no fucking idea what the server is storing
open fun writeFullStateCommit(buf: FriendlyByteBuf) {}
// client-side, meant to bring state forward
open fun processCommit(buf: FriendlyByteBuf) {}
open fun getComponent(): NNComponent? = null
}
// Used by the relay
// If the ComponentItem in the card slot
interface ConventionalNetworkDevice {
fun sendClassicPacket(packet: Networking.ClassicPacket)
}
abstract class WirelessEndpoint(address: UUID?) : DeviceNode(address) {
abstract fun getEndpointRange(): Double
abstract fun getEndpointDimension(): Int
abstract fun getEndpointLevel(): Level
abstract fun getEndpointPosition(): Vec3
// separate from process for simplicity
abstract fun receiveWireless(message: Message, emitter: WirelessEndpoint)
}

View File

@@ -1,7 +1,8 @@
package org.neoflock.neocomputers.network
import net.minecraft.core.BlockPos
import org.neoflock.neocomputers.NeoComputers
import net.minecraft.world.level.Level
import org.neoflock.neocomputers.entity.MachineEvent
import java.util.UUID
import kotlin.math.min
import kotlin.math.pow
@@ -27,322 +28,134 @@ object Networking {
enum class Visibility {
// only it can see itself
NONE,
// can only see its direct connections
DIRECT,
// some, as determined by getPreferredFew()
SOME,
// Can see everything network-wide
NETWORK,
}
abstract class Message(val sender: Node)
abstract class Message(val sender: DeviceNode)
class ClassicPacket(sender: Node, val src: String, val dst: String, val port: Int, val data: List<Any>, val hopCount: Int) : Message(sender) {
fun hop() = ClassicPacket(sender, src, dst, port, data, hopCount + 1);
// null dst means broadcast
class ClassicPacket(sender: DeviceNode, val src: String, val dst: String?, val port: Int, val data: List<Any>, val hopCount: Int) : Message(sender) {
fun hop(sender: DeviceNode) = ClassicPacket(sender, src, dst, port, data, hopCount + 1);
}
// for plugins and shi
class ComputerCheckedSignal(sender: Node, val player: String?, val name: String, val data: Array<Any>): Message(sender)
class ComputerUncheckedSignal(sender: Node, val name: String, val data: Array<Any>): Message(sender)
class ComputerCheckedSignal(sender: DeviceNode, val player: String?, val name: String, val data: Array<Any>): Message(sender)
class ComputerUncheckedSignal(sender: DeviceNode, val name: String, val data: Array<Any>): Message(sender)
class ComputerEvent(sender: DeviceNode, val machineEvent: MachineEvent): Message(sender)
open class Node(_address: UUID? = null) {
val connections = mutableSetOf<Node>()
private var reachableCache: Set<Node>? = null
var address = _address ?: UUID.randomUUID()
open var reachability = Visibility.NETWORK
open var powerRole = PowerRole.CONSUMER
open var energy: Long = 0
open var energyCapacity: Long = 0
// give energy, returns how much was actually given
// cannot exceed amount specified
open fun giveEnergy(amount: Long): Long {
val maximum = min(amount, energyCapacity - energy)
energy += maximum
return maximum
}
// take energy out, returns how much was actually taken
// cannot exceed amount specified
open fun withdrawEnergy(amount: Long): Long {
val maximum = min(amount, energy)
energy -= maximum
return maximum
}
fun getChargerNodes(): Set<Node> = getReachable().filter { it.powerRole != PowerRole.CONSUMER }.toSet()
fun totalEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energy }
fun maxEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energyCapacity }
// attempts to consume
fun consumeEnergy(energy: Long): Boolean {
// consumes energy, returns false if not enough
val total = totalEnergyInConnections() + this.energy
if(energy > total) return false
var remaining = energy
remaining -= withdrawEnergy(remaining)
if(remaining <= 0) return true
for (charger in getChargerNodes()) {
if(remaining <= 0) break
remaining -= charger.withdrawEnergy(remaining)
}
return true
}
// PLEASE only call if consumer, in the name of all that is holy
fun tryToChargeFully() {
var remaining = energyCapacity - energy
if(remaining <= 0) return
for (charger in getChargerNodes()) {
if(remaining <= 0) break
val amount = charger.withdrawEnergy(remaining)
val given = giveEnergy(amount)
remaining -= given
if(given < amount) {
val delta = amount - given // amount lost while given back
if(charger.giveEnergy(delta) < delta) {
NeoComputers.LOGGER.warn("LOSING ENERGY! Tried giving $delta back to $charger and we're losing our marbles!")
}
}
}
}
// only call if storage
fun balanceStorage() {
for(battery in getReachable()) {
if(battery.powerRole != PowerRole.STORAGE) continue
// its so if for example we have a battery with 2x the capacity
// we don't try to even the energy between them since that's just bad
// and might pointless delete energy over time
val capacityRatio = energyCapacity.toDouble() / battery.energyCapacity
val meaningfulSurplus = (battery.energy * capacityRatio - energy).toLong()
if(meaningfulSurplus <= 0) {
// WE'RE greedy (or negligible surplus)? Do nothing
continue
}
// steal from this greedy mf
val toSteal = meaningfulSurplus / 2
if(toSteal == 0L) continue // broke storahh
val stolen = battery.withdrawEnergy(toSteal)
if(giveEnergy(stolen) < stolen) {
NeoComputers.LOGGER.warn("LOSING ENERGY IN NODE $this!!!! THIS IS REALLY BAD!!!")
}
}
}
// rob the generators
fun stealGeneratorPower() {
var remaining = energyCapacity - energy
for(generator in getReachable()) {
if(generator.powerRole != PowerRole.GENERATOR) continue
// rob this mf
val robbed = generator.withdrawEnergy(remaining)
val taken = giveEnergy(robbed)
if(taken < robbed) {
NeoComputers.LOGGER.warn("energy caught being DELETED in the big 26")
}
remaining -= taken
}
}
open fun tick() {
if(powerRole == PowerRole.CONSUMER) tryToChargeFully()
if(powerRole == PowerRole.STORAGE) {
stealGeneratorPower()
balanceStorage()
}
}
// processes a received message
open fun received(message: Message) {}
// called when a new direct connection is made
open fun onConnect(node: Node) {}
// called when a direct connection is lost
open fun onDisconnect(node: Node) {}
// called when a new node is added globally
open fun onNodeAdded(node: Node) {
reachableCache = null;
}
// called when a node is removed globally
open fun onNodeRemoved(node: Node) {
reachableCache = null;
}
fun getReachable(): Set<Node> {
if(reachableCache == null) {
reachableCache = computeReachable();
}
return reachableCache!!;
}
fun invalidateReachableCache() {
reachableCache = null
}
fun computeReachable(): Set<Node> {
if(reachability == Visibility.NONE) {
return setOf();
}
if(reachability == Visibility.DIRECT) {
return connections.minus(this);
}
if(reachability == Visibility.NETWORK) {
// absolute cinema
val working = mutableSetOf<Node>();
val pending = mutableListOf(this);
var iterCount = 0;
while(iterCount < maxHopCount && pending.isNotEmpty()) {
iterCount++;
val subnode = pending.removeFirst();
if(subnode in working) continue;
working.add(subnode);
pending.addAll(subnode.connections);
}
// cannot send to itself!
working.remove(this);
return working;
}
throw NotImplementedError("visibility not implemented");
}
fun connectTo(other: Node) {
this.directConnectTo(other);
other.directConnectTo(this);
}
fun disconnectFrom(other: Node) {
this.directDisconnectFrom(other);
other.directDisconnectFrom(this);
}
fun directConnectTo(other: Node) {
if(other == this) return;
if(other in connections) return;
connections.add(other);
this.onConnect(other);
}
fun directDisconnectFrom(other: Node) {
if(other !in connections) return;
connections.remove(other);
this.onDisconnect(other);
}
}
abstract class WirelessEndpoint(address: UUID?) : Node(address) {
abstract fun getRange(): Double
abstract fun getDimension(): Int
abstract fun getPosition(): BlockPos
// separate from process for simplicity
abstract fun receiveWireless(message: Message, emitter: WirelessEndpoint)
}
val wirelessNodes = mutableSetOf<WirelessEndpoint>()
val allNodes = mutableMapOf<UUID, Node>()
val wirelessNodes = ThreadLocal.withInitial { HashSet<WirelessEndpoint>() }
val allNodes = ThreadLocal.withInitial { HashMap<UUID, DeviceNode>() }
// node may differ from message.sender in the case of relays,
// as they might have DIRECT reachability but
fun emitMessage(node: Node, message: Message) {
node.getReachable().forEach { it.received(message) }
fun emitMessage(deviceNode: DeviceNode, message: Message, exclude: Set<DeviceNode> = setOf()) {
deviceNode.getReachable().forEach { if(it !in exclude) it.received(message) }
}
fun computeRangeAllowedByHardness(src: BlockPos, dst: BlockPos): Double {
fun computeRangeAllowedByHardness(level: Level, src: BlockPos, dst: BlockPos): Double {
return Double.POSITIVE_INFINITY // TODO: math
}
fun distanceBetween(a: BlockPos, b: BlockPos): Double {
return sqrt((a.x - b.x + a.y - b.y + a.z - b.z).toDouble().pow(2.0));
}
fun emitWirelessMessage(starter: WirelessEndpoint, range: Double, message: Message) {
val startPos = starter.getPosition();
val startDim = starter.getDimension();
val range = starter.getRange();
wirelessNodes.forEach {
if(it.getDimension() != startDim) return;
val pos = it.getPosition();
val d = distanceBetween(startPos, pos);
var trueRange = min(it.getRange(), range);
trueRange = min(trueRange, computeRangeAllowedByHardness(startPos, pos));
if(d > trueRange) return;
it.receiveWireless(message, starter);
val startPos = starter.getEndpointPosition()
val startDim = starter.getEndpointDimension()
val level = starter.getEndpointLevel()
wirelessNodes.get().forEach {
if(it.getEndpointDimension() != startDim) return
val pos = it.getEndpointPosition()
val d = startPos.distanceTo(pos)
var trueRange = min(it.getEndpointRange(), range)
trueRange = min(trueRange, computeRangeAllowedByHardness(level, BlockPos.containing(startPos), BlockPos.containing(pos)))
if(d > trueRange) return
it.receiveWireless(message, starter)
}
}
fun tickAllNodes() {
allNodes.forEach { it.value.tick() }
allNodes.get().forEach { it.value.tick() }
tickCount++
}
fun getNode(address: UUID): Node? = allNodes[address]
fun getNode(address: UUID): DeviceNode? = allNodes.get()[address]
// TODO: use setter, more convenient
fun changeNodeAddress(node: Node, address: UUID) {
allNodes.remove(node.address)
node.address = address
allNodes[address] = node
fun changeNodeAddress(deviceNode: DeviceNode, address: UUID): Boolean {
if(deviceNode.address.equals(address)) return false
if(deviceNode.address !in allNodes.get()) return false
if(address in allNodes.get()) return false
allNodes.get().remove(deviceNode.address)
deviceNode.address = address
allNodes.get()[address] = deviceNode
return true
}
fun addNode(node: Node) {
if(node.address in allNodes) return;
allNodes[node.address] = node
if(node is WirelessEndpoint) {
wirelessNodes.add(node);
fun addNode(deviceNode: DeviceNode) {
if(deviceNode.address in allNodes.get()) return
allNodes.get()[deviceNode.address] = deviceNode
if(deviceNode is WirelessEndpoint) {
wirelessNodes.get().add(deviceNode);
}
// notify at the end so it is notified of its own creation
allNodes.forEach { it.value.onNodeAdded(node) }
allNodes.get().forEach { it.value.onNodeAdded(deviceNode) }
}
fun addNodes(vararg nodes: Node) {
nodes.forEach { addNode(it) }
fun addNodes(deviceNodes: Iterable<DeviceNode>) {
deviceNodes.forEach { addNode(it) }
}
fun removeNode(node: Node) {
if(node.address !in allNodes) return
allNodes.forEach { it.value.onNodeRemoved(node) }
fun addNodes(vararg deviceNodes: DeviceNode) {
addNodes(deviceNodes.asIterable())
}
fun removeNode(deviceNode: DeviceNode) {
if(deviceNode.address !in allNodes.get()) return
NodeSynchronizer.nodeErased(deviceNode)
allNodes.get().forEach { it.value.onNodeRemoved(deviceNode) }
// toList() in order to copy it
node.connections.toList().forEach {
node.disconnectFrom(it)
deviceNode.connections.toList().forEach {
deviceNode.disconnectFrom(it)
}
// actually remove at the end so it can listen to its own removal
allNodes.remove(node.address)
if(node is WirelessEndpoint) {
wirelessNodes.remove(node);
allNodes.get().remove(deviceNode.address)
if(deviceNode is WirelessEndpoint) {
wirelessNodes.get().remove(deviceNode);
}
}
fun removeNodes(vararg nodes: Node) {
nodes.forEach { removeNode(it) }
fun removeNodes(deviceNodes: Iterable<DeviceNode>) {
deviceNodes.forEach { removeNode(it) }
}
val channels = mutableMapOf<String, MutableSet<Node>>();
fun removeNodes(vararg deviceNodes: DeviceNode) {
removeNodes(deviceNodes.asIterable())
}
fun addToChannel(channel: String, node: Node) {
if(!channels.containsKey(channel)) {
channels[channel] = mutableSetOf();
val channels = ThreadLocal.withInitial { HashMap<String, MutableSet<DeviceNode>>() }
fun addToChannel(channel: String, deviceNode: DeviceNode) {
val localChannels = channels.get()
if(!localChannels.containsKey(channel)) {
localChannels[channel] = mutableSetOf();
}
channels[channel]!!.add(node);
localChannels[channel]!!.add(deviceNode);
}
fun removeFromChannel(channel: String, node: Node) {
if(!channels.containsKey(channel)) return;
channels[channel]?.remove(node);
if(channels[channel].isNullOrEmpty()) {
channels.remove(channel);
fun removeFromChannel(channel: String, deviceNode: DeviceNode) {
val localChannels = channels.get()
if(!localChannels.containsKey(channel)) return;
localChannels[channel]?.remove(deviceNode);
if(localChannels[channel].isNullOrEmpty()) {
localChannels.remove(channel);
}
}
fun emitChannelMessage(starter: Node, channel: String, message: Message) {
val c = channels[channel] ?: return;
fun emitChannelMessage(starter: DeviceNode, channel: String, message: Message) {
val localChannels = channels.get()
val c = localChannels[channel] ?: return;
c.forEach { if(it != starter) it.received(message); };
}
}

View File

@@ -0,0 +1,171 @@
package org.neoflock.neocomputers.network
import dev.architectury.networking.NetworkManager
import io.netty.buffer.Unpooled
import net.minecraft.core.BlockPos
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level
import org.neoflock.neocomputers.NeoComputers
import java.time.Duration
object NodeSynchronizer {
val MAX_STATE_DISTANCE_ALLOWED = 128
class DeviceBlockStatePayload(var blockPos: BlockPos, var buffers: List<FriendlyByteBuf>): CustomPacketPayload {
companion object {
val BLOCKDEV_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "blockdev_sync")
val TYPE = CustomPacketPayload.Type<DeviceBlockStatePayload>(BLOCKDEV_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStatePayload> {
override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStatePayload {
val blockPos = buf.readBlockPos()
val bufferCount = buf.readVarInt()
val buffers = List(bufferCount) {
val bytes = buf.readByteArray()
val rawBuf = Unpooled.buffer(bytes.size)
rawBuf.writeBytes(bytes)
FriendlyByteBuf(rawBuf)
}
return DeviceBlockStatePayload(blockPos, buffers)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: DeviceBlockStatePayload) {
buf.writeBlockPos(payload.blockPos)
buf.writeVarInt(payload.buffers.size)
payload.buffers.forEach {
buf.writeByteArray(it.array())
}
}
}
}
override fun type() = TYPE
}
class DeviceBlockStateRequest(var blockPos: BlockPos): CustomPacketPayload {
companion object {
val BLOCKDEV_REQ_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "blockdev_statereq")
val TYPE = CustomPacketPayload.Type<DeviceBlockStateRequest>(BLOCKDEV_REQ_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStateRequest> {
override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStateRequest {
val blockPos = buf.readBlockPos()
return DeviceBlockStateRequest(blockPos)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: DeviceBlockStateRequest) {
buf.writeBlockPos(payload.blockPos)
}
}
}
override fun type() = TYPE
}
class ScreenPayload(var buffer: FriendlyByteBuf): CustomPacketPayload {
companion object {
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")
val TYPE = CustomPacketPayload.Type<ScreenPayload>(SCREEN_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenPayload> {
override fun decode(buf: RegistryFriendlyByteBuf): ScreenPayload {
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
return ScreenPayload(buffer)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenPayload) {
buf.writeBytes(payload.buffer)
}
}
}
override fun type() = TYPE
}
class ScreenDataPayload(var buffer: FriendlyByteBuf): CustomPacketPayload {
companion object {
val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_DATA_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload {
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
return ScreenDataPayload(buffer)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenDataPayload) {
buf.writeBytes(payload.buffer)
}
}
}
override fun type() = TYPE
}
class BeepDataPayload(val pos: BlockPos, val pattern: String, val freq: Int, val duration: Duration, val volume: Double): CustomPacketPayload {
companion object {
val BEEP_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "beep_data")
val TYPE = CustomPacketPayload.Type<BeepDataPayload>(BEEP_DATA_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, BeepDataPayload> {
override fun decode(buf: RegistryFriendlyByteBuf): BeepDataPayload {
val pos = buf.readBlockPos()
val pattern = buf.readUtf()
val freq = buf.readVarInt()
val duration = buf.readVarLong()
val volume = buf.readDouble()
return BeepDataPayload(pos, pattern, freq, Duration.ofMillis(duration), volume)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: BeepDataPayload) {
buf.writeBlockPos(payload.pos)
buf.writeUtf(payload.pattern)
buf.writeVarInt(payload.freq)
buf.writeVarLong(payload.duration.toMillis())
buf.writeDouble(payload.volume)
}
}
}
override fun type() = TYPE
}
val screenMap = HashMap<ServerPlayer, DeviceNode>()
fun playerScreenClosed(player: ServerPlayer) {
screenMap.remove(player)
}
fun registerPlayerScreen(player: ServerPlayer, devNode: DeviceNode) {
screenMap[player] = devNode
}
fun nodeErased(node: DeviceNode) {
var player: ServerPlayer? = null
for((p, n) in screenMap) {
if(n == node) player = p
}
if(player != null) screenMap.remove(player)
}
fun syncScreens() {
for((player, ent) in screenMap) {
val buf = FriendlyByteBuf(Unpooled.buffer())
ent.encodeScreenData(player, buf)
NetworkManager.sendToPlayer(player, ScreenPayload(buf))
}
}
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
NetworkManager.sendToServer(ScreenDataPayload(friendlyByteBuf))
}
fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) {
if(level is ServerLevel) {
level.players().forEach {
NetworkManager.sendToPlayer(it, beepDataPayload)
}
}
}
}

View File

@@ -1,42 +1,46 @@
package org.neoflock.neocomputers.network
import net.minecraft.world.level.block.entity.BlockEntityType
import org.neoflock.neocomputers.block.DeviceBlockEntity
//? if fabric {
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext
import org.neoflock.neocomputers.block.NodeBlockEntity
/*import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext
import net.minecraft.core.Direction
import team.reborn.energy.api.EnergyStorage;
//?}
*///?}
// our soul purpose is to fuse bullshit power APIs together
// the NodeBlockEntity and Node given us a way to get power from a block, we just
// need to tell mods how to do it as well
object PowerManager {
fun<T: NodeBlockEntity> registerPowerBlockEntity(blockEntityType: BlockEntityType<T>) {
fun<T: DeviceBlockEntity> registerPowerDevice(blockEntityType: BlockEntityType<T>) {
//? if fabric {
EnergyStorage.SIDED.registerForBlockEntity({
entity, dir -> object : EnergyStorage {
override fun getAmount() = entity.node.energy
override fun getCapacity() = entity.node.energyCapacity
override fun supportsExtraction() = entity.node.powerRole != PowerRole.CONSUMER && entity.node.energyCapacity > 0
override fun supportsInsertion() = entity.node.powerRole != PowerRole.GENERATOR
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
if(entity.node.powerRole == PowerRole.CONSUMER) return 0
val taken = entity.node.withdrawEnergy(maxAmount)
transaction?.addCloseCallback {
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.node.giveEnergy(taken)
/*EnergyStorage.SIDED.registerForBlockEntity({
// TODO: as this is currently written, if the node instance changes and the mod cached the conversion, we're boned. Consider fixing it.
entity, dir ->
val node = entity.getNodeFromSide(dir ?: Direction.UP)
if(node == null) null else object : EnergyStorage {
override fun getAmount() = node.energy
override fun getCapacity() = node.energyCapacity
override fun supportsExtraction() = node.powerRole != PowerRole.CONSUMER && node.energyCapacity > 0
override fun supportsInsertion() = node.powerRole != PowerRole.GENERATOR
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
if(node.powerRole == PowerRole.CONSUMER) return 0
val taken = node.withdrawEnergy(maxAmount)
transaction?.addCloseCallback {
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) node.giveEnergy(taken)
}
return taken
}
return taken
}
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
if(entity.node.powerRole == PowerRole.GENERATOR) return 0
val given = entity.node.giveEnergy(maxAmount)
transaction?.addCloseCallback { ctx, res ->
if (res.wasAborted() || !res.wasCommitted()) entity.node.withdrawEnergy(given)
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
if(node.powerRole == PowerRole.GENERATOR) return 0
val given = node.giveEnergy(maxAmount)
transaction?.addCloseCallback { ctx, res ->
if (res.wasAborted() || !res.wasCommitted()) node.withdrawEnergy(given)
}
return given
}
return given
}
}
}, blockEntityType);
//?}
*///?}
}
}

View File

@@ -1,10 +1,20 @@
package org.neoflock.neocomputers.sounds
import dev.architectury.registry.registries.DeferredRegister
import net.minecraft.client.Minecraft
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.phys.Vec3
import org.lwjgl.BufferUtils
import org.neoflock.neocomputers.NeoComputers
import org.lwjgl.openal.AL10
import java.nio.ByteBuffer
import kotlin.experimental.xor
import kotlin.math.PI
import kotlin.math.max
import kotlin.math.sign
import kotlin.math.sin
object Sounds {
val SOUNDS = DeferredRegister.create(NeoComputers.MODID, Registries.SOUND_EVENT)!!
@@ -14,4 +24,140 @@ object Sounds {
fun registerSound(name: String) = SOUNDS.register(name) {
SoundEvent.createVariableRangeEvent(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, name))
}!!
val BEEP_SAMPLERATE = 44100
val BEEP_AMPLITUDE = 32f
val BEEP_MAXDIST = 16f
// Also largely taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala
val allSounds = ThreadLocal.withInitial { mutableListOf<CustomSoundBuffer>() }
class CustomSoundBuffer {
var dead: Boolean = true
var buffer: Int = -1
var source: Int = -1
fun start(x: Float, y: Float, z: Float, data: ByteBuffer, gain: Float): Int? {
// clear errors or smth idk
AL10.alGetError()
// written in a C style by a C dev
// all this work on a JVM project and I'm still writing C
// would be better if Kotlin had goto btw just saying
val ok = AL10.AL_NO_ERROR
var err = ok
buffer = AL10.alGenBuffers()
err = AL10.alGetError()
if(err != ok) return err
AL10.alBufferData(buffer, AL10.AL_FORMAT_MONO8, data, BEEP_SAMPLERATE)
err = AL10.alGetError()
if(err != ok) {
AL10.alDeleteBuffers(buffer)
return err
}
source = AL10.alGenSources()
err = AL10.alGetError()
if(err != ok) {
AL10.alDeleteBuffers(buffer)
return err
}
AL10.alSourceQueueBuffers(source, buffer)
err = AL10.alGetError()
if(err != ok) {
AL10.alDeleteBuffers(buffer)
AL10.alDeleteSources(source)
return err
}
AL10.alSource3f(source, AL10.AL_POSITION, x, y, z)
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, BEEP_MAXDIST)
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, BEEP_MAXDIST)
AL10.alSourcef(source, AL10.AL_GAIN, gain * 0.3f)
err = AL10.alGetError()
if(err != ok) {
AL10.alDeleteBuffers(buffer)
AL10.alDeleteSources(source)
return err
}
AL10.alSourcePlay(source)
err = AL10.alGetError()
if(err != ok) {
AL10.alDeleteBuffers(buffer)
AL10.alDeleteSources(source)
return err
}
dead = false
return null
}
fun checkDone(): Boolean {
if(dead) return true
if(AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) return false
dead = true
AL10.alDeleteSources(source)
AL10.alDeleteBuffers(buffer)
return true
}
}
fun beep(pos: Vec3, pattern: String, frequency: Int = 1000, duration: Int = 200) {
NeoComputers.LOGGER.info("Beep: $pattern, $frequency Hz, $duration ms")
val mc = Minecraft.getInstance()
val playerPos = mc.player?.position() ?: pos
val distanceBasedGain = max(0.0, 1 - pos.distanceTo(playerPos) / BEEP_MAXDIST).toFloat()
val volume = 1.0
val gain = distanceBasedGain * volume
if (gain <= 0 || BEEP_AMPLITUDE <= 0) return
// Algorithm effectively ported over from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala
// We do add support for spaces tho
val charArr = pattern.toCharArray()
val sampleCounts = charArr.map { if(it == '.') duration else 2 * duration }.map { it * BEEP_SAMPLERATE / 1000 }
val pauseSample = 50 * BEEP_SAMPLERATE / 1000
val finalBuf = BufferUtils.createByteBuffer(sampleCounts.sum() + pauseSample * sampleCounts.lastIndex)
val step = frequency.toFloat() / BEEP_SAMPLERATE
var off = 0f
for((i, sampleCount) in sampleCounts.withIndex()) {
if(charArr[i] == ' ') {
for(sample in 0..<sampleCount) {
finalBuf.put(127)
}
} else {
for(sample in 0..<sampleCount) {
val angle = 2 * PI * off
val value = (sin(angle).sign * BEEP_AMPLITUDE).toInt().toByte().xor(0x80.toByte())
off += step
if(off > 1) off -= 1f
finalBuf.put(value)
}
}
if(finalBuf.hasRemaining()) {
for(sample in 0..<pauseSample) {
finalBuf.put(127)
}
}
}
finalBuf.rewind()
val sound = CustomSoundBuffer()
val soundErr = sound.start(pos.x.toFloat(), pos.y.toFloat(), pos.z.toFloat(), finalBuf, gain.toFloat())
if(soundErr != null) {
NeoComputers.LOGGER.error("Playing beep failed, OpenAL exit code of $soundErr")
return
}
NeoComputers.LOGGER.info("Beeping with ${finalBuf.capacity()} samples")
allSounds.get().addLast(sound)
}
fun tickCustomSounds() {
allSounds.get().removeIf { it.checkDone() }
}
}

View File

@@ -1,8 +1,16 @@
package org.neoflock.neocomputers.utils
// based off the ImplementedContainer of https://docs.fabricmc.net/develop/blocks/block-containers
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.VertexFormat
import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.AbstractWidget
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderStateShard.ShaderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.world.Container;
import net.minecraft.core.NonNullList;
import net.minecraft.network.FriendlyByteBuf
@@ -67,7 +75,7 @@ abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var containe
// Based off the code in ChestMenu
for (i in 0..2) {
for (j in 0..8) {
this.addSlot(Slot(inventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18))
this.addSlot(Slot(inventory, j + i * 9 + 9, 8 + j * 18, y + i * 18))
}
}
@@ -127,6 +135,15 @@ abstract class GenericContainerScreen<T: GenericContainerMenu>(menu: T, inventor
val imageY: Int
get() = (height - imageHeight) / 2
var widgets = mutableListOf<AbstractWidget>()
val RENDER_TYPE = { r: ResourceLocation ->
RenderType.create("nc_gui_bg", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.QUADS, RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder()
.setShaderState(ShaderStateShard.POSITION_TEX_SHADER)
.setTextureState(RenderStateShard.TextureStateShard(r, false, false))
.createCompositeState(false))
}
override fun init() {
super.init()
@@ -137,24 +154,50 @@ abstract class GenericContainerScreen<T: GenericContainerMenu>(menu: T, inventor
val menuTex = findMenuTexture()
val cx = (width - imageWidth) / 2
val cy = (height - imageHeight) / 2
guiGraphics.pose().pushPose()
guiGraphics.pose().translate(cx.toFloat(), cy.toFloat(), 0f)
guiGraphics.blit(menuTex, imageX, imageY, 0, 0, imageWidth, imageHeight)
guiGraphics.blit(menuTex, 0, 0, 0, 0, imageWidth, imageHeight)
renderbg(guiGraphics, f, i-cx, j-cy)
for (widget in widgets) {
widget.render(guiGraphics, i-cx, j-cy, f)
}
for (slot in menu.slots) {
if (slot is DynamicSlot) {
// NeoComputers.LOGGER.info("slot")
slot.draw(guiGraphics, cx, cy, i, j)
slot.draw(guiGraphics, i-cx, j-cy)
}
}
guiGraphics.pose().popPose()
}
open fun renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) {
open fun renderbg(guiGraphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {}
}
open fun renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) { }
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
super.render(graphics, mouseX, mouseY, something)
renderCustomOverlay(graphics, mouseX, mouseY, something)
graphics.pose().pushPose()
graphics.pose().translate(imageX.toFloat(), imageY.toFloat(), 0f)
renderCustomOverlay(graphics, mouseX-imageX, mouseY-imageY, something)
graphics.pose().popPose() // not even doing this because it's better anymore, im just doing this because i dont want to change it back
if(shouldRenderTooltip()) super.renderTooltip(graphics, mouseX, mouseY)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
super.mouseClicked(mouseX, mouseY, button)
for (widget in widgets) {
if (widget.mouseClicked(mouseX-imageX, mouseY-imageY, button)) return true
}
return false
}
fun addWidget(widget: AbstractWidget) {
widgets.add(widget)
}
}

View File

@@ -0,0 +1,162 @@
package org.neoflock.neocomputers.utils
import com.mojang.blaze3d.platform.NativeImage
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.texture.DynamicTexture
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.ResourceLocationPattern
import org.neoflock.neocomputers.gui.widget.DynamicSlot
import kotlin.experimental.and
import kotlin.math.min
data class GPUChar(val c: Char, val fg: Int =0xFFFFFF, val bg: Int = 0) // all is bgr
// TODO: wrapper over NN buffer
class TextBuffer(var width: Int, var height: Int) {
// val CHARW = 8
// val CHARH = 16
//
// val texwidth: Int
// get() = width*CHARW
//
// val texheight: Int
// get() = height*CHARH
//
// var image = NativeImage(texwidth, texheight, true)
// var tex = DynamicTexture(image)
val blank = GPUChar(' ')
var buf = Array(width*height) { blank }
// init {
// Minecraft.getInstance().textureManager.register(this.id, tex)
// }
// fun toRGBA(color: Int): Int {
// // Minecaft lies, its AGBR
// return java.lang.Integer.reverseBytes((color.toLong() * 256 + 0xFF).toInt())
// }
//
// fun drawGlyph(x: Int, y: Int, c: Char, fg: Int) {
// var glyph: ArrayList<Byte> = FontProvider.map[c]!!
//
// for (j in 0..<CHARH) {
// for (i in 0..<CHARW) {
// // var pixel = ((glyph[j] and ((1 shl (CHARW - i - 1)).toByte())).toInt()) ushr (CHARW - i - 1) // retardation
// var pixel = (glyph[j] and (0b10000000 ushr i).toByte()).toInt()
// if (pixel > 0) image.setPixelRGBA(x+i, y+j, toRGBA(fg))
// }
// }
// }
//
// fun drawBuffer() {
// for (i in 0..<width) {
// for (j in 0..<height) {
// var char: GPUChar = buf[j*height+i]
// var x = i*CHARW
// var y = j*CHARH
// image.fillRect(x, y, CHARW, CHARH, toRGBA(char.bg))
// if (char.c != ' ' && char.c != '\u0000') drawGlyph(x, y, char.c, char.fg)
// }
// }
// tex.upload()
// }
//
// fun clean() {
// Minecraft.getInstance().textureManager.release(this.id)
// image.close()
// tex.close()
// }
fun encodeContents(buf: FriendlyByteBuf) {
// 0x01 means set fg, 0x02 means set bg,
// 0x03 means set char+count
var lastFg = 0xFFFFFF
var lastBg = 0x000000
buf.writeVarInt(width)
buf.writeVarInt(height)
var i = 0
while(i < this.buf.size) {
val px = this.buf[i]
if(px.fg != lastFg) {
buf.writeByte(0x01)
buf.writeVarInt(px.fg)
lastFg = px.fg
}
if(px.bg != lastBg) {
buf.writeByte(0x02)
buf.writeVarInt(px.bg)
lastBg = px.bg
}
var charWritten = 1
while((i+charWritten) < this.buf.size && this.buf[i+charWritten].c == px.c) charWritten++
buf.writeByte(0x03)
buf.writeVarInt(px.c.code)
buf.writeVarInt(charWritten)
i += charWritten
}
}
fun decodeContents(buf: FriendlyByteBuf) {
var lastFg = 0xFFFFFF
var lastBg = 0x000000
width = buf.readVarInt()
height = buf.readVarInt()
if(width*height != this.buf.size) {
this.buf = Array(width * height) { blank }
}
var i = 0
while(i < width*height) {
val op = buf.readByte().toInt()
if(op == 0x01) {
lastFg = buf.readVarInt()
}
if(op == 0x02) {
lastBg = buf.readVarInt()
}
if(op == 0x03) {
val c = buf.readVarInt().toChar()
val n = buf.readVarInt()
for(o in 0..<n) {
this.buf[i+o] = GPUChar(c, lastFg, lastBg)
}
i += n
}
}
}
fun inBounds(x: Int, y: Int) = x >= 0 && y >= 0 && x < width && y < height
fun get(x: Int, y: Int) = if(inBounds(x, y)) buf[x+y*width] else blank
fun _set(x: Int, y: Int, pixel: GPUChar) {
if(!inBounds(x, y)) return
buf[x+y*width] = pixel
// image.fillRect(x, y, CHARW, CHARH, toRGBA(pixel.bg))
// if (pixel.c != ' ' && pixel.c != '\u0000') drawGlyph(x, y, pixel.c, pixel.fg)
}
fun set(x: Int, y: Int, text: String, fg: Int = 0xFFFFFF, bg: Int = 0x000000, vertical: Boolean = false) {
for ((i, c) in text.toCharArray().withIndex()) {
val cx = if(vertical) x else x + i
val cy = if(vertical) y + i else y
_set(cx, cy, GPUChar(c, fg, bg))
}
// tex.upload()
}
fun fill(x: Int, y: Int, w: Int, h: Int, pixel: GPUChar = blank) {
// turn it into values we can fw
val fw = min(w, width)
val fh = min(h, height)
for(py in y..<y+fh) {
for (px in x..<x + fw) {
_set(px, py, pixel)
}
}
// tex.upload()
}
}

View File

@@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "neocomputers:block/assembler" }
}
}

View File

@@ -0,0 +1,94 @@
{
"multipart": [
{ "apply": { "model": "neocomputers:block/cable/cable_center" }},
{ "when": { "south": true },
"apply": { "model": "neocomputers:block/cable/cable_connection" }},
{ "when": { "east": true},
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": -90}},
{ "when": { "west": true },
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": 90}},
{ "when": { "north": true },
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": 180}},
{ "when": { "up": true },
"apply": { "model": "neocomputers:block/cable/cable_connection", "x": 90}},
{ "when": { "down": true },
"apply": { "model": "neocomputers:block/cable/cable_connection", "x": -90}},
{
"when": {
"south": false,
"north": false,
"east": false,
"west": false,
"up": false,
"down": false
},
"apply": { "model": "neocomputers:block/cable/cable_caps" }
},
{
"when": {
"south": true,
"north": false,
"east": false,
"west": false,
"up": false,
"down": false
},
"apply": { "model": "neocomputers:block/cable/cable_cap" }
},
{
"when": {
"south": false,
"north": true,
"east": false,
"west": false,
"up": false,
"down": false
},
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": 180 }
},
{
"when": {
"south": false,
"north": false,
"east": true,
"west": false,
"up": false,
"down": false
},
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": -90 }
},
{
"when": {
"south": false,
"north": false,
"east": false,
"west": true,
"up": false,
"down": false
},
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": 90 }
},
{
"when": {
"south": false,
"north": false,
"east": false,
"west": false,
"up": true,
"down": false
},
"apply": { "model": "neocomputers:block/cable/cable_cap", "x": 90 }
},
{
"when": {
"south": false,
"north": false,
"east": false,
"west": false,
"up": false,
"down": true
},
"apply": { "model": "neocomputers:block/cable/cable_cap", "x": -90 }
}
]
}

View File

@@ -0,0 +1,19 @@
{
"variants": {
"facing=north": {
"model": "neocomputers:block/case"
},
"facing=east": {
"model": "neocomputers:block/case",
"y": 90
},
"facing=west": {
"model": "neocomputers:block/case",
"y": -90
},
"facing=south": {
"model": "neocomputers:block/case",
"y": 180
}
}
}

View File

@@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "neocomputers:block/rack"}
}
}

View File

@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "neocomputers:block/relay"
}
}
}

View File

@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "neocomputers:block/robot"
}
}
}

View File

@@ -1,7 +1,57 @@
{
"variants": {
"": {
"facing_horiz=north,facing_verti=1": {
"model": "neocomputers:block/screen"
},
"facing_horiz=east,facing_verti=1": {
"model": "neocomputers:block/screen",
"y": 90
},
"facing_horiz=west,facing_verti=1": {
"model": "neocomputers:block/screen",
"y": -90
},
"facing_horiz=south,facing_verti=1": {
"model": "neocomputers:block/screen",
"y": 180
},
"facing_horiz=north,facing_verti=0": {
"model": "neocomputers:block/screen",
"x": 90
},
"facing_horiz=east,facing_verti=0": {
"model": "neocomputers:block/screen",
"x": 90,
"y": 90
},
"facing_horiz=west,facing_verti=0": {
"model": "neocomputers:block/screen",
"x": 90,
"y": -90
},
"facing_horiz=south,facing_verti=0": {
"model": "neocomputers:block/screen",
"x": 90,
"y": 180
},
"facing_horiz=north,facing_verti=2": {
"model": "neocomputers:block/screen",
"x": -90
},
"facing_horiz=east,facing_verti=2": {
"model": "neocomputers:block/screen",
"x": -90,
"y": 90
},
"facing_horiz=west,facing_verti=2": {
"model": "neocomputers:block/screen",
"x": -90,
"y": -90
},
"facing_horiz=south,facing_verti=2": {
"model": "neocomputers:block/screen",
"x": -90,
"y": 180
}
}
}

View File

@@ -10,6 +10,9 @@
"block.neocomputers.capacitor": "Small Capacitor",
"block.neocomputers.capacitor2": "Medium Capacitor",
"block.neocomputers.capacitor3": "Large Capacitor",
"block.neocomputers.case": "Case (Untiered)",
"block.neocomputers.cable": "Cable",
"block.neocomputers.relay": "Relay",
"item.neocomputers.eeprom0": "EEPROM (Tier 1)",
"item.neocomputers.cpu0": "CPU (Tier 1)",
"item.neocomputers.cpu1": "CPU (Tier 2)",
@@ -28,6 +31,7 @@
"item.neocomputers.lan": "Wired Network Card",
"item.neocomputers.wlan0": "Wireless Network Card (Tier 1)",
"item.neocomputers.wlan1": "Wireless Network Card (Tier 2)",
"item.neocomputers.tunnel": "Linked Card",
"item.neocomputers.data0": "Data Card (Tier 1)",
"item.neocomputers.data1": "Data Card (Tier 2)",
"item.neocomputers.data2": "Data Card (Tier 3)",
@@ -37,15 +41,27 @@
"item.neocomputers.hdd0": "Hard Disk Drive (Tier 1)",
"item.neocomputers.hdd1": "Hard Disk Drive (Tier 2)",
"item.neocomputers.hdd2": "Hard Disk Drive (Tier 3)",
"neocomputers.errors.ENOCPU": "no CPU",
"neocomputers.errors.E2BIG": "too many components",
"neocomputers.errors.ENOENJ": "dangerously low energy",
"neocomputers.errors.ENOMEM": "missing memory",
"neocomputers.wlan.range": "Range: %1$s blocks",
"neocomputers.data.limit": "Memory: %1$s",
"neocomputers.gpu.vram": "Video Memory: %1$spx",
"neocomputers.disk.spaceused": "Space Used: %1$s / %2$s",
"neocomputers.gpu.vram": "Video memory: %1$spx",
"neocomputers.disk.spaceused": "Space used: %1$s / %2$s",
"neocomputers.readonly": "Read-Only",
"neocomputers.readwrite": "Read-Write",
"neocomputers.noaddr": "No address assigned",
"neocomputers.computer.on": "ON",
"neocomputers.computer.off": "OFF",
"neocomputers.computer.errorNoMsg": "Error: ",
"neocomputers.computer.energy": "Energy: %1$s / %2$s J",
"neocomputers.computer.memory": "Memory: %1$s / %2$s",
"neocomputers.computer.components": "Components: %1$s / %2$s",
"neocomputers.memory.capacity": "Capacity: %1$s",
"neocomputers.eeprom.codeused": "Code Storage: %1$s / %2$s",
"neocomputers.eeprom.dataused": "Data Storage: %1$s / %2$s",
"sounds.neocomputers.computer_running": "Computer Fans"
"neocomputers.arch": "Architecture: %1$s",
"neocomputers.eeprom.codeused": "Code stored: %1$s / %2$s",
"neocomputers.eeprom.dataused": "Data stored: %1$s / %2$s",
"neocomputers.tunnel.channel": "Linked channel: %1$s",
"sounds.neocomputers.computer_running": "Computer fans"
}

View File

@@ -0,0 +1,67 @@
{
"neocomputers.confirm": "Confirmer",
"neocomputers.cancel": "Annuler",
"block.neocomputers.combustgen": "Générateur à combustion",
"block.neocomputers.redio": "Controleur de redstone",
"item.neocomputers.redio0": "Carte redstone (niveau 1)",
"item.neocomputers.redio1": "Carte redstone (niveau 2)",
"block.neocomputers.solargen": "Panneau solaire",
"block.neocomputers.screen": "Écran (sans niveau)",
"block.neocomputers.capacitor": "Petit condensateur",
"block.neocomputers.capacitor2": "Condensateur moyen",
"block.neocomputers.capacitor3": "Grand condensateur",
"block.neocomputers.case": "Boîtier (sans niveau)",
"block.neocomputers.cable": "Cable",
"block.neocomputers.relay": "Relais",
"item.neocomputers.eeprom0": "EEPROM (niveau 1)",
"item.neocomputers.cpu0": "Processeur (niveau 1)",
"item.neocomputers.cpu1": "Processeur (niveau 2)",
"item.neocomputers.cpu2": "Processeur (niveau 3)",
"item.neocomputers.cbus0": "Bus informatique (niveau 1)",
"item.neocomputers.cbus1": "Bus informatique (niveau 2)",
"item.neocomputers.cbus2": "Bus informatique (niveau 3)",
"item.neocomputers.cbus_creative": "Bus informatique (créatif)",
"item.neocomputers.memory0": "Mémoire (niveau 1)",
"item.neocomputers.memory1": "Mémoire (niveau 1,5)",
"item.neocomputers.memory2": "Mémoire (niveau 2)",
"item.neocomputers.memory3": "Mémoire (niveau 2,5)",
"item.neocomputers.memory4": "Mémoire (niveau 3)",
"item.neocomputers.memory5": "Mémoire (niveau 3,5)",
"item.neocomputers.inet": "Carte internet",
"item.neocomputers.lan": "Carte réseau sans fil",
"item.neocomputers.wlan0": "Carte réseau sans fil (niveau 1)",
"item.neocomputers.wlan1": "Carte réseau sans fil (niveau 2)",
"item.neocomputers.tunnel": "Carte liée",
"item.neocomputers.data0": "Carte de données (niveau 1)",
"item.neocomputers.data1": "Carte de données (niveau 2)",
"item.neocomputers.data2": "Carte de données (niveau 3)",
"item.neocomputers.gpu0": "Carte graphiques (niveau 1)",
"item.neocomputers.gpu1": "Carte graphiques (niveau 2)",
"item.neocomputers.gpu2": "Carte graphiques (niveau 3)",
"item.neocomputers.hdd0": "Disque dur (niveau 1)",
"item.neocomputers.hdd1": "Disque dur (niveau 2)",
"item.neocomputers.hdd2": "Disque dur (niveau 3)",
"neocomputers.errors.ENOCPU": "absence d'un processeur",
"neocomputers.errors.E2BIG": "trop de composants",
"neocomputers.errors.ENOENJ": "niveau d'énergie dangereusement bas",
"neocomputers.errors.ENOMEM": "absence de mémoire",
"neocomputers.wlan.range": "Portée: %1$s blocs",
"neocomputers.data.limit": "Mémoire: %1$s",
"neocomputers.gpu.vram": "Mémoire vidéo: %1$spx",
"neocomputers.disk.spaceused": "Espace utilisé: %1$s / %2$s",
"neocomputers.readonly": "Lecture seule",
"neocomputers.readwrite": "Lecture-écriture",
"neocomputers.noaddr": "Pas d'adresse attribué",
"neocomputers.computer.on": "ALLUMÉ",
"neocomputers.computer.off": "ÉTEINT",
"neocomputers.computer.errorNoMsg": "Erreur: ",
"neocomputers.computer.energy": "Énergie: %1$s / %2$s J",
"neocomputers.computer.memory": "Mémoire: %1$s / %2$s",
"neocomputers.computer.components": "Composants: %1$s / %2$s",
"neocomputers.memory.capacity": "Capacité: %1$s",
"neocomputers.arch": "Architecture: %1$s",
"neocomputers.eeprom.codeused": "Code stocké: %1$s / %2$s",
"neocomputers.eeprom.dataused": "Données stockées: %1$s / %2$s",
"neocomputers.tunnel.channel": "Chaîne connectée: %1$s",
"sounds.neocomputers.computer_running": "Ventilateurs d'ordinateur"
}

View File

@@ -0,0 +1,67 @@
{
"neocomputers.confirm": "Bevestigen",
"neocomputers.cancel": "Annuleren",
"block.neocomputers.combustgen": "Verbrandingsgenerator",
"block.neocomputers.redio": "Redstone I/O",
"item.neocomputers.redio0": "Redstonekaart (niveau 1)",
"item.neocomputers.redio1": "Redstonekaart (niveau 2)",
"block.neocomputers.solargen": "Zonnegenerator",
"block.neocomputers.screen": "Scherm (niveauloos)",
"block.neocomputers.capacitor": "Kleine condensator",
"block.neocomputers.capacitor2": "Middelgrote condensator",
"block.neocomputers.capacitor3": "Grote condensator",
"block.neocomputers.case": "Behuizing (niveauloos)",
"block.neocomputers.cable": "Kabel",
"block.neocomputers.relay": "Doorgever",
"item.neocomputers.eeprom0": "EEPROM (niveau 1)",
"item.neocomputers.cpu0": "Processor (niveau 1)",
"item.neocomputers.cpu1": "Processor (niveau 2)",
"item.neocomputers.cpu2": "Processor (niveau 3)",
"item.neocomputers.cbus0": "Componentenbus (niveau 1)",
"item.neocomputers.cbus1": "Componentenbus (niveau 2)",
"item.neocomputers.cbus2": "Componentenbus (niveau 3)",
"item.neocomputers.cbus_creative": "Componentenbus (creatief)",
"item.neocomputers.memory0": "Geheugen (niveau 1)",
"item.neocomputers.memory1": "Geheugen (niveau 1,5)",
"item.neocomputers.memory2": "Geheugen (niveau 2)",
"item.neocomputers.memory3": "Geheugen (niveau 2,5)",
"item.neocomputers.memory4": "Geheugen (niveau 3)",
"item.neocomputers.memory5": "Geheugen (niveau 3,5)",
"item.neocomputers.inet": "Internetkaart",
"item.neocomputers.lan": "Bedrade netwerkkaart",
"item.neocomputers.wlan0": "Draadloze netwerkkaart (niveau 1)",
"item.neocomputers.wlan1": "Draadloze netwerkkaart (niveau 2)",
"item.neocomputers.tunnel": "Gekoppelde kaart",
"item.neocomputers.data0": "Datakaart (niveau 1)",
"item.neocomputers.data1": "Datakaart (niveau 2)",
"item.neocomputers.data2": "Datakaart (niveau 3)",
"item.neocomputers.gpu0": "Videokaart (niveau 1)",
"item.neocomputers.gpu1": "Videokaart (niveau 2)",
"item.neocomputers.gpu2": "Videokaart (niveau 3)",
"item.neocomputers.hdd0": "Harde schijf (niveau 1)",
"item.neocomputers.hdd1": "Harde schijf (niveau 2)",
"item.neocomputers.hdd2": "Harde schijf (niveau 3)",
"neocomputers.errors.ENOCPU": "geen processor",
"neocomputers.errors.E2BIG": "te veel componenten",
"neocomputers.errors.ENOENJ": "gevaarlijk weinig stroom",
"neocomputers.errors.ENOMEM": "geen geheugen",
"neocomputers.wlan.range": "Bereik: %1$s blokken",
"neocomputers.data.limit": "Geheugen: %1$s",
"neocomputers.gpu.vram": "Videogeheugen: %1$spx",
"neocomputers.disk.spaceused": "Gebruikte ruimte: %1$s / %2$s",
"neocomputers.readonly": "Alleen-lezen",
"neocomputers.readwrite": "Lezen-schrijven",
"neocomputers.noaddr": "Geen adres toegewezen",
"neocomputers.computer.on": "AAN",
"neocomputers.computer.off": "UIT",
"neocomputers.computer.errorNoMsg": "Fout: ",
"neocomputers.computer.energy": "Stroom: %1$s / %2$s J",
"neocomputers.computer.memory": "Geheugen: %1$s / %2$s",
"neocomputers.computer.components": "Componenten: %1$s / %2$s",
"neocomputers.memory.capacity": "Capaciteit: %1$s",
"neocomputers.arch": "Architectuur: %1$s",
"neocomputers.eeprom.codeused": "Opgeslagen code: %1$s / %2$s",
"neocomputers.eeprom.dataused": "Opgeslagen data: %1$s / %2$s",
"neocomputers.tunnel.channel": "Gekoppeld kanaal: %1$s",
"sounds.neocomputers.computer_running": "Computerventilatoren"
}

View File

@@ -0,0 +1,68 @@
{
"neocomputers.ro_ro.diacritics": "ĂăÂâÎîȘșȚț",
"neocomputers.confirm": "Confirmă",
"neocomputers.cancel": "Anulează",
"block.neocomputers.combustgen": "Generator de Ardere",
"block.neocomputers.redio": "Controlor Redstone",
"item.neocomputers.redio0": "Placă Redstone (Grad 1)",
"item.neocomputers.redio1": "Placă Redstone (Grad 2)",
"block.neocomputers.solargen": "Generator Solar",
"block.neocomputers.screen": "Ecran (fără grad)",
"block.neocomputers.capacitor": "Condensator Electric Mic",
"block.neocomputers.capacitor2": "Condensator Electric Mediu",
"block.neocomputers.capacitor3": "Condensator Electric Mare",
"block.neocomputers.case": "Carcasă (fără grad)",
"block.neocomputers.cable": "Cabluri",
"block.neocomputers.relay": "Releu",
"item.neocomputers.eeprom0": "Memorie Programabilă Electronic (Grad 1)",
"item.neocomputers.cpu0": "Procesor Central (Grad 1)",
"item.neocomputers.cpu1": "Procesor Central (Grad 2)",
"item.neocomputers.cpu2": "Procesor Central (Grad 3)",
"item.neocomputers.cbus0": "Magistrală de Componente (Grad 1)",
"item.neocomputers.cbus1": "Magistrală de Componente (Grad 2)",
"item.neocomputers.cbus2": "Magistrală de Componente (Grad 3)",
"item.neocomputers.cbus_creative": "Magistrală de Componente (Creativ)",
"item.neocomputers.memory0": "Memorie (Grad 1)",
"item.neocomputers.memory1": "Memorie (Grad 1.5)",
"item.neocomputers.memory2": "Memorie (Grad 2)",
"item.neocomputers.memory3": "Memorie (Grad 2.5)",
"item.neocomputers.memory4": "Memorie (Grad 3)",
"item.neocomputers.memory5": "Memorie (Grad 3.5)",
"item.neocomputers.inet": "Placă de Internet",
"item.neocomputers.lan": "Placă de Rețea pe Fir",
"item.neocomputers.wlan0": "Placă de Rețea fără Fir (Grad 1)",
"item.neocomputers.wlan1": "Placă de Rețea fără Fir (Grad 2)",
"item.neocomputers.tunnel": "Placă Legată",
"item.neocomputers.data0": "Placă de Date (Grad 1)",
"item.neocomputers.data1": "Placă de Date (Grad 2)",
"item.neocomputers.data2": "Placă de Date (Grad 3)",
"item.neocomputers.gpu0": "Placă Video (Grad 1)",
"item.neocomputers.gpu1": "Placă Video (Grad 2)",
"item.neocomputers.gpu2": "Placă Video (Grad 3)",
"item.neocomputers.hdd0": "Disc Dur (Grad 1)",
"item.neocomputers.hdd1": "Disc Dur (Grad 2)",
"item.neocomputers.hdd2": "Disc Dur (Grad 3)",
"neocomputers.errors.ENOCPU": "fără procesor",
"neocomputers.errors.E2BIG": "prea multe componente",
"neocomputers.errors.ENOENJ": "prea puțină energie",
"neocomputers.errors.ENOMEM": "fără memorie",
"neocomputers.wlan.range": "Distanță maximă: %1$s blocks",
"neocomputers.data.limit": "Memorie: %1$s",
"neocomputers.gpu.vram": "Memorie video: %1$spx",
"neocomputers.disk.spaceused": "Spațiu utilizat: %1$s / %2$s",
"neocomputers.readonly": "Citit-Doar",
"neocomputers.readwrite": "Citit-Scris",
"neocomputers.noaddr": "Fără adresă",
"neocomputers.computer.on": "PORNIT",
"neocomputers.computer.off": "OPRIT",
"neocomputers.computer.errorNoMsg": "Problemă: ",
"neocomputers.computer.energy": "Energie: %1$s / %2$s J",
"neocomputers.computer.memory": "Memorie: %1$s / %2$s",
"neocomputers.computer.components": "Componente: %1$s / %2$s",
"neocomputers.memory.capacity": "Capacitate: %1$s",
"neocomputers.arch": "Arhitectură: %1$s",
"neocomputers.eeprom.codeused": "Cod ținut: %1$s / %2$s",
"neocomputers.eeprom.dataused": "Date ținute: %1$s / %2$s",
"neocomputers.tunnel.channel": "Canal de legătură: %1$s",
"sounds.neocomputers.computer_running": "Ventilatoare"
}

View File

@@ -0,0 +1,44 @@
{
"parent": "block/block",
"textures": {
"bottom": "neocomputers:block/generic_top",
"top": "neocomputers:block/assembler_top",
"side": "neocomputers:block/assembler_side",
"particle": "neocomputers:block/generic_top"
},
"elements": [
{ "from": [ 0, 0, 0 ],
"to": [ 16, 7, 16 ],
"faces": {
"up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" },
"down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom" },
"north": { "uv": [ 0, 0, 16, 7 ], "texture": "#side" },
"south": { "uv": [ 0, 0, 16, 7 ], "texture": "#side" },
"west": { "uv": [ 0, 0, 16, 7 ], "texture": "#side" },
"east": { "uv": [ 0, 0, 16, 7 ], "texture": "#side" }
}
},
{ "from": [ 2, 7, 2 ],
"to": [ 14, 9, 14 ],
"faces": {
"up": { "uv": [ 2, 2, 14, 14 ], "texture": "#top" },
"down": { "uv": [ 2, 2, 14, 14 ], "texture": "#bottom" },
"north": { "uv": [ 2, 7, 14, 9 ], "texture": "#side" },
"south": { "uv": [ 2, 7, 14, 9 ], "texture": "#side" },
"west": { "uv": [ 2, 7, 14, 9 ], "texture": "#side" },
"east": { "uv": [ 2, 7, 14, 9 ], "texture": "#side" }
}
},
{ "from": [ 0, 9, 0 ],
"to": [ 16, 16, 16 ],
"faces": {
"up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" },
"down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom" },
"north": { "uv": [ 0, 9, 16, 16 ], "texture": "#side" },
"south": { "uv": [ 0, 9, 16, 16 ], "texture": "#side" },
"west": { "uv": [ 0, 9, 16, 16 ], "texture": "#side" },
"east": { "uv": [ 0, 9, 16, 16 ], "texture": "#side" }
}
}
]
}

View File

@@ -0,0 +1,22 @@
{
"parent": "minecraft:block/block",
"render_type": "minecraft:solid",
"textures": {
"cap": "neocomputers:block/cable_cap",
"particle": "neocomputers:block/cable_cap"
},
"elements": [
{
"from": [6, 6, 5],
"to": [10, 10, 6],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [0,0,4,4], "texture": "#cap" },
"down": { "uv": [0,0,4,4], "texture": "#cap" },
"north": { "uv": [6, 6, 10, 10], "texture": "#cap"}
}
}
]
}

View File

@@ -0,0 +1,82 @@
{
"parent": "minecraft:block/block",
"render_type": "minecraft:solid",
"textures": {
"cap": "neocomputers:block/cable_cap",
"particle": "neocomputers:block/cable_cap"
},
"elements": [
{
"from": [6, 6, 5],
"to": [10, 10, 6],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [0,0,4,4], "texture": "#cap" },
"down": { "uv": [0,0,4,4], "texture": "#cap" },
"north": { "uv": [6,6,10,10], "texture": "#cap"},
"south": { "uv": [0,0,4,4], "texture": "#cap"}
}
},
{
"from": [6, 6, 10],
"to": [10, 10, 11],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [0,0,4,4], "texture": "#cap" },
"down": { "uv": [0,0,4,4], "texture": "#cap" },
"north": { "uv": [0,0,4,4], "texture": "#cap"},
"south": { "uv": [6,6,10,10], "texture": "#cap"}
}
},
{
"from": [5, 6, 6],
"to": [6, 10, 10],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [6,6,10,10], "texture": "#cap" },
"up": { "uv": [0,0,4,4], "texture": "#cap" },
"down": { "uv": [0,0,4,4], "texture": "#cap" },
"north": { "uv": [0,0,4,4], "texture": "#cap"},
"south": { "uv": [0,0,4,4], "texture": "#cap"}
}
},
{
"from": [10, 6, 6],
"to": [11, 10, 10],
"faces": {
"east": { "uv": [6,6,10,10], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [0,0,4,4], "texture": "#cap" },
"down": { "uv": [0,0,4,4], "texture": "#cap" },
"north": { "uv": [0,0,4,4], "texture": "#cap"},
"south": { "uv": [0,0,4,4], "texture": "#cap"}
}
},
{
"from": [6, 5, 6],
"to": [10, 6, 10],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [0,0,4,4], "texture": "#cap" },
"down": { "uv": [6,6,10,10], "texture": "#cap" },
"north": { "uv": [0,0,4,4], "texture": "#cap"},
"south": { "uv": [0,0,4,4], "texture": "#cap"}
}
},
{
"from": [6, 10, 6],
"to": [10, 11, 10],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [6,6,10,10], "texture": "#cap" },
"down": { "uv": [0,0,4,4], "texture": "#cap" },
"north": { "uv": [0,0,4,4], "texture": "#cap"},
"south": { "uv": [0,0,4,4], "texture": "#cap"}
}
}
]
}

View File

@@ -0,0 +1,22 @@
{
"parent": "minecraft:block/block",
"render_type": "minecraft:solid",
"textures": {
"body": "neocomputers:block/cable_body",
"particle": "neocomputers:block/cable_cap"
},
"elements": [
{
"from": [6, 6, 6],
"to": [10, 10, 10],
"faces": {
"north": { "uv": [6,6,10,10], "texture": "#body", "tintindex": 0 },
"east": { "uv": [6,6,10,10], "texture": "#body", "tintindex": 0 },
"west": { "uv": [6,6,10,10], "texture": "#body", "tintindex": 0 },
"south": { "uv": [6,6,10,10], "texture": "#body", "tintindex": 0 },
"up": { "uv": [0,0, 5, 5], "texture": "#body", "tintindex": 0 },
"down": { "uv": [0,0, 5, 5], "texture": "#body", "tintindex": 0 }
}
}
]
}

View File

@@ -0,0 +1,22 @@
{
"parent": "minecraft:block/block",
"render_type": "minecraft:solid",
"textures": {
"cap": "neocomputers:block/cable_cap",
"body": "neocomputers:block/cable_body",
"particle": "neocomputers:block/cable_cap"
},
"elements": [
{
"from": [6, 6, 10],
"to": [10, 10, 16],
"faces": {
"east": { "uv": [4,4,11,11], "texture": "#body", "tintindex": 0 },
"west": { "uv": [4,4,11,11], "texture": "#body", "tintindex": 0 },
"up": { "uv": [4,4,11,11], "texture": "#body", "tintindex": 0 },
"down": { "uv": [4,4,11,11], "texture": "#body", "tintindex": 0 },
"south": { "uv": [6, 6, 10, 10], "texture": "#cap", "tintindex": 0}
}
}
]
}

View File

@@ -0,0 +1,45 @@
{
"parent": "minecraft:block/block",
"render_type": "minecraft:solid",
"textures": {
"cap": "neocomputers:block/cable_cap",
"body": "neocomputers:block/cable_body",
"particle": "neocomputers:block/cable_cap"
},
"elements": [
{
"from": [6, 1, 6],
"to": [10, 15, 10],
"faces": {
"east": { "uv": [0,0,5,16], "texture": "#body", "tintindex": 0 },
"west": { "uv": [0,0,5,16], "texture": "#body", "tintindex": 0 },
"north": { "uv": [0,0,5,16], "texture": "#body", "tintindex": 0 },
"south": { "uv": [0,0,5,16], "texture": "#body", "tintindex": 0 },
"up": { "uv": [0,0,5,16], "texture": "#body", "tintindex": 0 },
"down": { "uv": [0,0,5,16], "texture": "#body", "tintindex": 0 }
}
},
{
"from": [4, 0, 4],
"to": [12, 1, 12],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [0,0,4,4], "texture": "#cap" },
"down": { "uv": [4, 4, 12, 12], "texture": "#cap" },
"north": { "uv": [0,0,4,4], "texture": "#cap"}
}
},
{
"from": [4, 15, 4],
"to": [12, 16, 12],
"faces": {
"east": { "uv": [0,0,4,4], "texture": "#cap" },
"west": { "uv": [0,0,4,4], "texture": "#cap" },
"up": { "uv": [4, 4, 12, 12], "texture": "#cap" },
"down": { "uv": [0,0,4,4], "texture": "#cap" },
"north": { "uv": [0,0,4,4], "texture": "#cap"}
}
}
]
}

View File

@@ -0,0 +1,12 @@
{
"parent": "minecraft:block/cube",
"textures": {
"up": "neocomputers:block/case_top",
"down": "neocomputers:block/case_bottom",
"north": "neocomputers:block/case_front",
"south": "neocomputers:block/case_back",
"east": "neocomputers:block/case_side",
"west": "neocomputers:block/case_side",
"particle": "neocomputers:block/case_top"
}
}

Some files were not shown because too many files have changed in this diff Show More