Compare commits

...

59 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
279 changed files with 3212 additions and 997 deletions

37
TODO.md
View File

@@ -1,31 +1,21 @@
# Networking
> Pretty important here
> All that is left is optimization
## Auto-connect
## Mergeable device nodes
Rework the auto-connect system of device blocks to be more stable.
Prob re-scan the network continuously
For optimization of screen grouping and cables, we should add a system that allows merging nodes
## Synchronization
## 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
Move the state logic into the node, as it was meant to be.
Note, addresses on the client are allowed to be complete bullshit at least for a bit of time.
`ComponentItem`s should no longer allow a node on the client (at least for now), it serves no
purpose currently, and also is not designed well.
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.
## Get rid of NodeBlockEntity
## Optimize power balancing
It is basically throw-away stitched together garbage.
Replace it with a new abstract class, which allows different nodes on different sides,
handles synchronizing those nodes according to their state changes.
Also implement an equivalent for LivingEntities, like drones or other addon stuff.
## Optimizations
Optimize the networking, both synchronization using smaller encodings,
and emitting messages or adding/removing nodes. The goal is to have as little of
the CPU time on the server thread taken by NC as we can.
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
@@ -38,3 +28,8 @@ Also because it is a capable engine and has a good API for architectures, and NC
## 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

@@ -13,8 +13,7 @@ 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.CaseEntityRenderer;
import org.neoflock.neocomputers.entity.render.ScreenEntityRenderer;
import org.neoflock.neocomputers.entity.render.*;
import org.neoflock.neocomputers.item.Items;
import org.neoflock.neocomputers.platforms.fabric.client.model.ModelLoader;
@@ -22,8 +21,6 @@ public class NeoComputersFabricClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
ModelLoadingPlugin.register(new ModelLoader());
BlockEntityRenderers.register(BlockEntities.INSTANCE.getSCREEN_ENTITY().get(), ScreenEntityRenderer::new);
BlockEntityRenderers.register(BlockEntities.INSTANCE.getCASE_ENTITY().get(), CaseEntityRenderer::new);
ColorProviderRegistry.BLOCK.register((state, world, pos, index) -> {
if (index == 0) {

View File

@@ -1,138 +0,0 @@
//package org.neoflock.neocomputers.platforms.fabric.client.model;
//
//import net.fabricmc.fabric.api.renderer.v1.Renderer;
//import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
//import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
//import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
//import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
//import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
//import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
//import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
//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.*;
//import net.minecraft.core.BlockPos;
//import net.minecraft.core.Direction;
//import net.minecraft.resources.ResourceLocation;
//import net.minecraft.util.RandomSource;
//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 java.util.ArrayList;
//import java.util.Collection;
//import java.util.List;
//import java.util.function.Function;
//import java.util.function.Supplier;
//
//// this totally could have been done with datagen, why do i do this
//public class CableModel implements BakedModel, UnbakedModel, FabricBakedModel {
//// private TextureAtlasSprite sprite = Minecraft
//// TextureAtlasSprite sprite = atlas.apply(new Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.withDefaultNamespace("block/furnace_top")));
//
// private float MIN = 6/16F;
// private float MAX = 10/16F;
//
// private BlockState state;
//
// private Mesh mesh;
// @Override
// public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) {
//// NeoComputers.INSTANCE.getLOGGER().info("Obtained blockstate!");
// this.state = state;
// return List.of();
// }
//
// @Override
// public boolean useAmbientOcclusion() {
// return false;
// }
//
// @Override
// public boolean isGui3d() {
// return true;
// }
//
// @Override
// public boolean usesBlockLight() {
// return true;
// }
//
// @Override
// public boolean isCustomRenderer() {
// return false;
// }
//
// @Override
// public TextureAtlasSprite getParticleIcon() {
// Function<ResourceLocation, TextureAtlasSprite> atlas = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS);
// return atlas.apply(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/teto"));
// }
//
// @Override
// public ItemTransforms getTransforms() {
// return ItemTransforms.NO_TRANSFORMS;
// }
//
// @Override
// public ItemOverrides getOverrides() {
// return ItemOverrides.EMPTY;
// }
//
// @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) {
// TextureAtlasSprite sprite = spriteGetter.apply(new Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/teto")));
//
// Renderer renderer = RendererAccess.INSTANCE.getRenderer();
// MeshBuilder builder = renderer.meshBuilder();
// QuadEmitter emitter = builder.getEmitter();
//
// bakeCenter(emitter, sprite);
//
//
// mesh = builder.build();
// return this;
// }
//
// @Override
// public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<RandomSource> randomSupplier, RenderContext context) {
// mesh.outputTo(context.getEmitter());
// }
//
// public void bakeCenter(QuadEmitter emitter, TextureAtlasSprite sprite) {
// for (Direction dir : Direction.values()) {
// emitter.square(dir, MIN, MIN, MAX, MAX, MIN);
// emitter.spriteBake(sprite, MutableQuadView.BAKE_LOCK_UV);
// emitter.color(-1, -1, -1, -1);
// emitter.emit();
// }
// }
// public void bakeConnection(Direction dir, QuadEmitter emitter, TextureAtlasSprite sprite) {
// int mag = dir.getStepX()+dir.getStepZ(); // i dont want to hear it
// float bottom = dir.getStepY()==0 ? 6/16F : (dir.getStepY()==1 ? 10/16F : 0F);
//
// for (Direction d : dir.getAxis().getPlane().faces) {
// emitter.square(d, (6/16F)+0.5F*mag, bottom, 6/16F)
// }
//
// }
//
//// @Override
//// public boolean isVanillaAdapter() {
//// return false; // TODO: let this be true so maybe forge and fabric can be unified
//// }
//}

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

@@ -1,24 +1,22 @@
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 CABLE = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "cable");
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().getNamespace().equals(NeoComputers.MODID)) NeoComputers.INSTANCE.getLOGGER().info("{} {} {}", id.id().getNamespace(), id.id().getPath(), id.id().getPath().equals(CABLE.id().getPath()));
// if (id != null && id.id().equals(CABLE)) {
////// NeoComputers.INSTANCE.getLOGGER().error("DOING CABLEEEEEEE");
// original.
// } else {
// return original;
// }
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,7 +1,5 @@
package org.neoflock.neocomputers
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.VertexFormat
import dev.architectury.event.events.client.ClientLifecycleEvent
import dev.architectury.event.events.common.LifecycleEvent
import dev.architectury.event.events.common.PlayerEvent
@@ -15,24 +13,22 @@ 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.RenderStateShard
import net.minecraft.client.renderer.RenderType
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.gui.buffer.BufferRenderer
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.GPUCard
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
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.nio.Buffer
object NeoComputers {
const val MODID: String = "neocomputers"
@@ -58,10 +54,14 @@ 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"))
@@ -87,12 +87,6 @@ object NeoComputers {
Networking.channels.remove()
}
ClientLifecycleEvent.CLIENT_STARTED.register {
Networking.allNodes.remove()
Networking.wirelessNodes.remove()
Networking.channels.remove()
}
PlayerEvent.CLOSE_MENU.register {
player, menu ->
if(player is ServerPlayer) NodeSynchronizer.playerScreenClosed(player)
@@ -111,19 +105,35 @@ object NeoComputers {
packet, ctx ->
val player = ctx.player
if(player is ServerPlayer) {
NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer)
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)
}
})
@@ -143,7 +153,7 @@ object NeoComputers {
}}
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)
}}

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
@@ -34,6 +35,10 @@ object Blocks {
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

@@ -24,9 +24,10 @@ 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() : BaseBlock(Properties.of()), EntityBlock {
class CableBlock() : DeviceBlock(Properties.of()), EntityBlock {
companion object {
val NORTH = BooleanProperty.create("north")
val EAST = BooleanProperty.create("east")
@@ -40,23 +41,36 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
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 makeShapes() { // screw perf
// for (north in arrayOf(false, true)) { // shut up
// for (east in arrayOf(false, true)) {
// for (west in arrayOf(false, true)) {
// for (south 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 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)
@@ -82,12 +96,20 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
fun shouldConnect(pos: BlockPos, npos: BlockPos, level: Level): Boolean {
val ent = level.getBlockEntity(npos)
val blockState = level.getBlockState(pos)
val theirState = level.getBlockState(npos)
return (ent is CableEntity &&
(level.getBlockState(npos).getValue(COLOR).equals(blockState.getValue(COLOR)) ||
blockState.getValue(COLOR).equals(DyeColor.LIGHT_GRAY) ||
level.getBlockState(npos).getValue(COLOR).equals(DyeColor.LIGHT_GRAY)))
// val state: BlockState? = (ent is CableEntity && (level.getBlockState(neighborPos).getValue(COLOR).equals(state.getValue(COLOR)) || state.getValue(COLOR).equals(DyeColor.LIGHT_GRAY))
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
}
}
@@ -101,6 +123,7 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
.setValue(DOWN, false)
.setValue(COLOR, DyeColor.LIGHT_GRAY)
)
makeShapes()
}
@@ -115,12 +138,12 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
.add(COLOR))
}
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
return CableEntity(pos, state)
}
override fun newBlockEntity(pos: BlockPos, state: BlockState) = CableEntity(pos, state)
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? {
return makeShape(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN))
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) {
@@ -129,6 +152,7 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
// 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)
@@ -141,6 +165,10 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
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,9 +1,10 @@
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.FriendlyByteBuf
import net.minecraft.network.chat.OutgoingChatMessage
import net.minecraft.network.chat.PlayerChatMessage
import net.minecraft.world.InteractionResult
@@ -18,16 +19,30 @@ 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 deviceNode = object : DeviceNode() {
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)
deviceNode.energy = min(compoundTag.getLong("energy"), deviceNode.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) {
@@ -40,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)
@@ -48,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(
@@ -59,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.deviceNode.giveEnergy(1)
val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.computeEdges().size} edges, ${ent.deviceNode.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,37 +3,25 @@ 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.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
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.FurnaceBlock
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")!!
@@ -47,7 +35,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(COMPUTER_RUNNING, false))
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState).initNetworking()
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState)
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
builder.add(COMPUTER_RUNNING)
@@ -58,17 +46,19 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
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,
@@ -78,7 +68,7 @@ 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? {
@@ -115,7 +105,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
} else {
// Open menu
MenuRegistry.openMenu(player as ServerPlayer, ent)
NodeSynchronizer.registerPlayerScreen(player, 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,304 +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.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
import org.neoflock.neocomputers.network.DeviceNode
import java.time.Duration
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_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 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
}
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, 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))
}
fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) {
if(level is ServerLevel) {
level.players().forEach {
NetworkManager.sendToPlayer(it, beepDataPayload)
}
}
}
}
abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) {
abstract val deviceNode: DeviceNode
fun initNetworking(): NodeBlockEntity {
Networking.addNode(deviceNode)
invalidateNodeState()
return this
}
// runs on the server, meant to encode state to send to all players
open fun encodeDownstreamData(packet: FriendlyByteBuf) {
packet.writeUUID(deviceNode.address)
packet.writeLong(deviceNode.energy)
packet.writeLong(deviceNode.energyCapacity)
packet.writeEnum(deviceNode.reachability)
packet.writeEnum(deviceNode.powerRole)
}
// runs on the client, meant to decode server state packets to synchronize client state
open fun syncWithUpstream(packet: FriendlyByteBuf) {
Networking.changeNodeAddress(deviceNode, packet.readUUID())
deviceNode.energy = packet.readLong()
deviceNode.energyCapacity = packet.readLong()
deviceNode.reachability = packet.readEnum(deviceNode.reachability.javaClass)
deviceNode.powerRole = packet.readEnum(deviceNode.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
}
open 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 {
deviceNode.connectTo(it.deviceNode)
}
}
override fun setChanged() {
invalidateNodeState()
computeEdges().forEach { it.invalidateNodeState() }
super.setChanged()
}
override fun setRemoved() {
super.setRemoved()
Networking.removeNode(deviceNode)
}
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
if(Networking.getNode(blockEntity.deviceNode.address) == null) blockEntity.initNetworking()
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

@@ -15,9 +15,7 @@ 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}
@@ -29,7 +27,7 @@ class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnt
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)
}
@@ -41,12 +39,12 @@ class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnt
}
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
Networking.emitMessage(deviceNode, Networking.ComputerUncheckedSignal(deviceNode, "redstone_changed", arrayOf(deviceNode.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? {
@@ -67,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)
}
@@ -125,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

@@ -26,23 +26,19 @@ import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.gui.menu.ScreenMenu
import kotlin.math.abs
import kotlin.math.max
import org.neoflock.neocomputers.network.NodeSynchronizer
class ScreenBlock() : NodeBlock() {
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
val ENERGY: Long = 5
}
init {
registerDefaultState(stateDefinition.any().setValue(FACING_HORIZ, Direction.NORTH).setValue(FACING_VERTI, 1))
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? {
val scr = ScreenEntity(blockPos, blockState)
scr.initNetworking()
return scr
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = ScreenEntity(blockPos, blockState)
override fun useWithoutItem(
blockState: BlockState,
@@ -52,14 +48,9 @@ class ScreenBlock() : NodeBlock() {
blockHitResult: BlockHitResult
): InteractionResult {
if(!level.isClientSide) {
val screenState = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
if(!screenState.deviceNode.consumeEnergy(ENERGY)) {
player.sendSystemMessage(Component.literal("Not enough power."))
return InteractionResult.SUCCESS
};
val sp = player as ServerPlayer
val ent = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
NodeSynchronizer.registerPlayerScreen(sp, ent)
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 {

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
@@ -87,12 +88,36 @@ object BlockEntities {
)
}
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.registerPowerBlockEntity(CASE_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

@@ -2,21 +2,46 @@ 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.NodeBlockEntity
import org.neoflock.neocomputers.block.DeviceBlockEntity
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.network.DeviceNode
class CableEntity(pos: BlockPos, state: BlockState) : BlockEntity(BlockEntities.CABLE_ENTITY.get(), pos, state) {
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

@@ -11,6 +11,7 @@ import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundSource
import net.minecraft.world.Container
import net.minecraft.world.ContainerHelper
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
@@ -20,12 +21,11 @@ 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.NodeSynchronizer
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
@@ -34,8 +34,10 @@ 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
@@ -48,45 +50,72 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
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)
packet.writeVarInt(diskActivityTime)
packet.writeVarInt(networkActivityTime)
packet.writeUtf(err ?: "")
}
override fun syncWithUpstream(packet: FriendlyByteBuf) {
super.syncWithUpstream(packet)
setRunning(packet.readBoolean())
diskActivityTime = packet.readVarInt()
networkActivityTime = packet.readVarInt()
err = packet.readUtf().ifEmpty { null }
}
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)
packet.writeByteArray((err ?: "").encodeToByteArray())
packet.writeLong(deviceNode.energy)
packet.writeLong(deviceNode.energyCapacity)
packet.writeLong(getMachineMemoryUsed())
packet.writeLong(getMachineMemoryTotal())
packet.writeLong(getMachineComponentsUsed())
packet.writeLong(getMachineComponentsTotal())
packet.writeUtf(arch)
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}
@@ -95,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)
}
@@ -118,7 +147,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
sendMachineEvent(MachineRedstoneEvent(this, dir, oldValue, newValue))
Networking.emitMessage(deviceNode, Networking.ComputerUncheckedSignal(deviceNode, "redstone_changed", arrayOf(deviceNode.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
@@ -134,6 +163,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
fun setRunning(value: Boolean) {
if(isOn == value) return
deviceNode.markChanged()
NeoComputers.LOGGER.info("[${deviceNode.address}] Going from $isOn to $value")
isOn = value
val world = level ?: return
@@ -150,14 +180,15 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
}
// Server-side stuff!!
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 archs = getMachineArchitectures()
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(archs.isEmpty()) {
if(architectures.isEmpty()) {
crash("@neocomputers.errors.ENOCPU")
beepAsync("-..")
return false
@@ -179,9 +210,9 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
beepAsync("-.")
return false
}
if(arch !in archs) {
if(arch !in architectures) {
// Just pick one! TODO: consult EEPROM first
arch = archs.first()
arch = architectures.first()
}
beepAsync(".")
setRunning(true)
@@ -195,10 +226,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
}
override fun crash(error: String): Boolean {
if(isOn) {
beepAsync("--")
sendMachineEvent(MachineCrashEvent(this, error))
}
beepAsync("--")
sendMachineEvent(MachineCrashEvent(this, error))
setRunning(false)
err = error
return true
@@ -208,12 +237,12 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
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
@@ -234,7 +263,7 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
override fun getMachineMemoryTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getMemoryCapacity(it) }.sum().toLong()
override fun getMachineMemoryUsed(): Long = 0
override fun getMachineComponentsUsed(): Long = deviceNode.getReachable().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()
@@ -251,11 +280,11 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
override fun stillValid(player: Player): Boolean = !this.isRemoved
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
deviceNode.energy = min(deviceNode.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) {
@@ -273,20 +302,6 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
super.setRemoved()
}
override fun tickNode(level: Level) {
super.tickNode(level)
if(!level.isClientSide) {
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 (!deviceNode.consumeEnergy(1)) {
crash("@neocomputers.errors.ENOENJ")
}
}
}
}
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,16 +16,15 @@ 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.DeviceNode
import org.neoflock.neocomputers.network.Networking
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
@@ -32,6 +32,11 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
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)
@@ -46,8 +51,8 @@ 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
@@ -79,11 +84,6 @@ 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(deviceNode.energy)
packet.writeLong(deviceNode.energyCapacity)
}
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))

View File

@@ -4,9 +4,7 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.level.Level
import net.minecraft.world.phys.Vec3
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking
import java.time.Duration
abstract class MachineEvent {
@@ -17,12 +15,16 @@ data class MachineRedstoneEvent(override val machine: MachineEntity, val side: D
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 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,
@@ -40,8 +42,6 @@ interface MachineEntity {
fun crash(error: String): Boolean
fun getLastError(): String?
fun getMachineNode(): DeviceNode
// Some metadata
fun getMachineMemoryTotal(): Long
fun getMachineMemoryUsed(): Long

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,79 +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.NeoComputers
import org.neoflock.neocomputers.block.NodeBlockEntity
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.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) {
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) {
// return if not directly connected
if(message.sender !in this.connections) return
val mEnv = message.machineEvent
NeoComputers.LOGGER.info("Got message $mEnv!")
if(mEnv is MachinePowerEvent) {
if(mEnv.nowRunning) {
textBuf.set(0, 0, address.toString())
} else {
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
private var cleanRenderer: () -> Unit = { } // TODO: THIS SUCKS, FIND A BETTER WAY
override fun encodeDownstreamData(packet: FriendlyByteBuf) {
super.encodeDownstreamData(packet)
textBuf.encodeContents(packet)
}
override fun syncWithUpstream(packet: FriendlyByteBuf) {
super.syncWithUpstream(packet)
textBuf.decodeContents(packet)
}
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
super.encodeScreenData(player, packet)
textBuf.encodeContents(packet)
}
override fun tickNode(level: Level) {
super.tickNode(level)
cleanrenderer()
createscreenstuffs()
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()
cleanRenderer()
}
private fun createscreenstuffs() {
private fun createScreenTexture() {
bound = "screen/"+deviceNode.address.toString().replace("-", "_")
if (level!!.isClientSide) {
var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf)
renderer.drawBuffer()
cleanrenderer = { renderer.clean() }
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,25 +1,25 @@
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.block.SingleDeviceBlockEntity
import org.neoflock.neocomputers.network.DeviceNode
import org.neoflock.neocomputers.network.Networking
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 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) {
deviceNode.giveEnergy(energyPerTick)

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

@@ -5,12 +5,14 @@ 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
@@ -23,9 +25,10 @@ class CaseEntityRenderer(private val context: BlockEntityRendererProvider.Contex
val BLINKTIME: Long = 10 // in ticks
val RENDER_TYPE = RenderType.create("nc_case", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS,
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_SHADER)
.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) {
@@ -38,20 +41,20 @@ class CaseEntityRenderer(private val context: BlockEntityRendererProvider.Contex
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)
} else drawLED(buffer, mat.last(), 3F, OFF)
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)
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) {
buffer.addVertex(mat, width/16F, 0F, 0F).setColor(color)
buffer.addVertex(mat, width/16F, 1/16F, 0F).setColor(color)
buffer.addVertex(mat, 0F, 1/16F, 0F).setColor(color)
buffer.addVertex(mat, 0F, 0F, 0F).setColor(color)
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) {

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

@@ -27,6 +27,7 @@ class ScreenEntityRenderer(val context: BlockEntityRendererProvider.Context?) :
.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

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

@@ -10,21 +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

@@ -13,7 +13,7 @@ import java.io.File
import kotlin.experimental.and
import kotlin.experimental.xor
class BufferRenderer(private var id: ResourceLocation, private var buffer: TextBuffer) { // TODO: NN buffer
class BufferRenderer(private var id: ResourceLocation, var buffer: TextBuffer) { // TODO: NN buffer
val CHARW = 8
val CHARH = 16
@@ -26,15 +26,11 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
Minecraft.getInstance().textureManager.register(this.id, tex)
}
fun dump(path: String) {
image.writeToFile(File(path))
NeoComputers.LOGGER.info("DUMPED!!!")
fun toRGBA(color: Int): Int {
// Minecaft lies, its AGBR
return java.lang.Integer.reverseBytes((color.toLong() * 256 + 0xFF).toInt())
}
// fun toRGBA(color: Int): Int {
// return color.shl(8).or(0xFF)
// }
fun drawGlyph(x: Int, y: Int, c: Char, fg: Int) {
var glyph: ArrayList<Byte> = FontProvider.map[c]!!
@@ -42,7 +38,7 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
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, 0xFF000000.toInt()+fg)
if (pixel > 0) image.setPixelRGBA(x+i, y+j, toRGBA(fg))
}
}
}
@@ -53,7 +49,7 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
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)
}
}

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

@@ -11,7 +11,7 @@ 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
@@ -26,6 +26,7 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
private var btn: ImagerButton? = null
override fun shouldCenterTitle(): Boolean = false
// override fun findMenuTexture(): ResourceLocation = BG
var isOn = false
var lastError: String? = null
@@ -70,14 +71,14 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
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, maxEnergy).withStyle(if(components <= maxComponents) ChatFormatting.WHITE else ChatFormatting.RED))
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)
) {
@@ -85,17 +86,11 @@ 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 renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) {
@@ -105,13 +100,4 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
}
}
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
if (button == 0 && btn!!.isHovered) {
btn!!.playDownSound(Minecraft.getInstance().soundManager)
btn!!.onClick(mouseX, mouseY)
return true
}
return super.mouseClicked(mouseX, mouseY, button)
}
}

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

@@ -24,6 +24,8 @@ class ScreenScreen : GenericContainerScreen<ScreenMenu>{
var textBuf = TextBuffer(0, 0)
override fun shouldCenterTitle(): Boolean = false
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
super.processScreenStatePacket(buf)
textBuf.decodeContents(buf)
@@ -44,6 +46,8 @@ class ScreenScreen : GenericContainerScreen<ScreenMenu>{
}
}
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,9 +2,8 @@ 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) = setOf(ComponentRoles.BUS)
@@ -13,7 +12,7 @@ open class CBUSItem(val tier: Int, val maxComponents: Int): Item(Item.Properties
override fun getComponentCapacity(itemStack: ItemStack) = maxComponents
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
}
class CBUS0: CBUSItem(1, 8)
class CBUS1: CBUSItem(2, 12)

View File

@@ -2,11 +2,10 @@ 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
@@ -15,7 +14,9 @@ open class CPUItem(val tier: Int, val maxComponents: Int): Item(Item.Properties(
override fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf("Lua 5.3")
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity?) = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun getRelayInterval(itemStack: ItemStack) = 4 / tier
}
class CPU0: CPUItem(1, 8)

View File

@@ -1,6 +1,7 @@
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
@@ -18,7 +19,7 @@ interface ComponentItem {
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
@@ -27,14 +28,14 @@ interface ComponentItem {
}
// 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
// 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?): DeviceNode?
fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?): DeviceNode?
// Gets the node associated to an item, if it exists
fun getComponentNode(itemStack: ItemStack): DeviceNode? {
@@ -52,3 +53,11 @@ 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) {
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?) = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

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
import java.nio.ByteBuffer
@@ -27,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) {
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?) = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

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) {
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?) = 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) {
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?) = 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) {
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?) = 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,13 +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.DeviceNode
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
@@ -20,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?) = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,
@@ -33,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) {
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?) = 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) {
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?) = 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 {

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
class TunnelCard: Item(Properties().component(DataComponents.TUNNEL_CHANNEL, "creative")), ComponentItem {
// yes, we're counting TUNNEL as a conventional networking card
@@ -14,13 +13,13 @@ class TunnelCard: Item(Properties().component(DataComponents.TUNNEL_CHANNEL, "cr
override fun getComponentTier(itemStack: ItemStack): Int = 3
override fun whenComponentPlaced(itemStack: ItemStack, machine: MachineEntity?, newRole: String) {
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?) = null
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
override fun appendHoverText(
itemStack: ItemStack,

View File

@@ -1,7 +1,9 @@
package org.neoflock.neocomputers.network
import net.minecraft.core.BlockPos
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
@@ -9,6 +11,9 @@ 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
@@ -145,77 +150,84 @@ open class DeviceNode(_address: UUID? = null) {
fun getReachable(): Set<DeviceNode> {
if(reachableCache == null) {
reachableCache = computeReachable();
reachableCache = computeReachable()
}
return reachableCache!!;
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();
return setOf()
}
if(reachability == Visibility.DIRECT) {
return connections.minus(this);
if(reachability == Visibility.SOME) {
return getPreferredFew()
}
if(reachability == Visibility.NETWORK) {
// absolute cinema
val working = HashSet<DeviceNode>();
val pending = mutableListOf(this);
var iterCount = 0;
val working = HashSet<DeviceNode>()
val pending = mutableListOf(this)
var iterCount = 0
while(iterCount < maxHopCount && pending.isNotEmpty()) {
iterCount++;
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.DIRECT) {
working.addAll(subnode.connections)
} else if(subnode.reachability == Visibility.SOME) {
pending.addAll(subnode.getPreferredFew())
}
}
// cannot send to itself!
working.remove(this);
return working;
working.remove(this)
return working
}
throw NotImplementedError("visibility not implemented");
throw NotImplementedError("visibility not implemented")
}
fun connectTo(other: DeviceNode) {
this.directConnectTo(other);
other.directConnectTo(this);
this.directConnectTo(other)
other.directConnectTo(this)
}
fun disconnectFrom(other: DeviceNode) {
this.directDisconnectFrom(other);
other.directDisconnectFrom(this);
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);
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);
if(other !in connections) return
connections.remove(other)
this.onDisconnect(other)
invalidateReachableCache()
}
// Network synchronization with the NodeSynchronizer
// TODO: process shi
var outOfSync = true
var outOfSync = false
fun markChanged() {
outOfSync = true
}
open fun encodeScreenData(buf: FriendlyByteBuf) {}
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
@@ -223,13 +235,22 @@ open class DeviceNode(_address: UUID? = null) {
// 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 getRange(): Double
abstract fun getDimension(): Int
abstract fun getPosition(): BlockPos
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,6 +1,7 @@
package org.neoflock.neocomputers.network
import net.minecraft.core.BlockPos
import net.minecraft.world.level.Level
import org.neoflock.neocomputers.entity.MachineEvent
import java.util.UUID
import kotlin.math.min
@@ -27,8 +28,8 @@ 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,
}
@@ -36,8 +37,9 @@ object Networking {
abstract class Message(val sender: DeviceNode)
class ClassicPacket(sender: DeviceNode, 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
@@ -50,30 +52,26 @@ object Networking {
// node may differ from message.sender in the case of relays,
// as they might have DIRECT reachability but
fun emitMessage(deviceNode: DeviceNode, message: Message) {
deviceNode.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();
val startPos = starter.getEndpointPosition()
val startDim = starter.getEndpointDimension()
val level = starter.getEndpointLevel()
wirelessNodes.get().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);
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)
}
}
@@ -85,12 +83,14 @@ object Networking {
fun getNode(address: UUID): DeviceNode? = allNodes.get()[address]
// TODO: use setter, more convenient
fun changeNodeAddress(deviceNode: DeviceNode, address: UUID) {
if(deviceNode.address.equals(address)) return
if(deviceNode.address !in allNodes.get()) return
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(deviceNode: DeviceNode) {
@@ -103,12 +103,17 @@ object Networking {
allNodes.get().forEach { it.value.onNodeAdded(deviceNode) }
}
fun addNodes(vararg deviceNodes: DeviceNode) {
fun addNodes(deviceNodes: Iterable<DeviceNode>) {
deviceNodes.forEach { addNode(it) }
}
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
deviceNode.connections.toList().forEach {
@@ -121,10 +126,14 @@ object Networking {
}
}
fun removeNodes(vararg deviceNodes: DeviceNode) {
fun removeNodes(deviceNodes: Iterable<DeviceNode>) {
deviceNodes.forEach { removeNode(it) }
}
fun removeNodes(vararg deviceNodes: DeviceNode) {
removeNodes(deviceNodes.asIterable())
}
val channels = ThreadLocal.withInitial { HashMap<String, MutableSet<DeviceNode>>() }
fun addToChannel(channel: String, deviceNode: DeviceNode) {

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.deviceNode.energy
override fun getCapacity() = entity.deviceNode.energyCapacity
override fun supportsExtraction() = entity.deviceNode.powerRole != PowerRole.CONSUMER && entity.deviceNode.energyCapacity > 0
override fun supportsInsertion() = entity.deviceNode.powerRole != PowerRole.GENERATOR
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
if(entity.deviceNode.powerRole == PowerRole.CONSUMER) return 0
val taken = entity.deviceNode.withdrawEnergy(maxAmount)
transaction?.addCloseCallback {
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.deviceNode.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.deviceNode.powerRole == PowerRole.GENERATOR) return 0
val given = entity.deviceNode.giveEnergy(maxAmount)
transaction?.addCloseCallback { ctx, res ->
if (res.wasAborted() || !res.wasCommitted()) entity.deviceNode.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,9 +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
@@ -68,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))
}
}
@@ -128,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()
@@ -138,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

@@ -1,14 +1,73 @@
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,
@@ -75,16 +134,19 @@ class TextBuffer(var width: Int, var height: Int) {
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) {
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))
_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
@@ -92,8 +154,9 @@ class TextBuffer(var width: Int, var height: Int) {
val fh = min(h, height)
for(py in y..<y+fh) {
for (px in x..<x + fw) {
set(px, py, pixel)
_set(px, py, pixel)
}
}
// tex.upload()
}
}

View File

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

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

@@ -10,7 +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)",

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

@@ -11,7 +11,9 @@
"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)",

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

@@ -2,7 +2,7 @@
"parent": "minecraft:block/block",
"render_type": "minecraft:solid",
"textures": {
"body": "neocomputers:block/cable_body"
"body": "neocomputers:block/cable_body",
"particle": "neocomputers:block/cable_cap"
},
"elements": [

View File

@@ -0,0 +1,70 @@
{
"parent": "minecraft:block/block",
"render_type": "minecraft:solid",
"textures": {
"side": "neocomputers:block/rack_side",
"top": "neocomputers:block/generic_top"
},
"elements": [
{
"from": [0, 0, 0],
"to": [1, 16, 16],
"faces": {
"west": { "uv": [0, 0, 16, 16], "texture": "side", "tint_index": 0 },
"east": { "uv": [0, 0, 16, 16], "texture": "side", "tint_index": 0 },
"north": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 },
"south": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 },
"up": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 },
"down": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 }
}
},
{
"from": [1, 0, 0],
"to": [16, 16, 1],
"faces": {
"north": { "uv": [0, 0, 15, 16], "texture": "side", "tint_index": 0 },
"south": { "uv": [1, 0, 16, 16], "texture": "side", "tint_index": 0 },
"west": { "uv": [0, 0, 1, 16], "texture": "side", "tint_index": 0 },
"east": { "uv": [0, 0, 1, 16], "texture": "side", "tint_index": 0 },
"up": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 },
"down": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 }
}
},
{
"from": [1, 0, 15],
"to": [16, 16, 16],
"faces": {
"north": { "uv": [0, 0, 15, 16], "texture": "side", "tint_index": 0 },
"south": { "uv": [1, 0, 16, 16], "texture": "side", "tint_index": 0 },
"west": { "uv": [0, 0, 1, 16], "texture": "side", "tint_index": 0 },
"east": { "uv": [0, 0, 1, 16], "texture": "side", "tint_index": 0 },
"up": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 },
"down": { "uv": [0, 0, 16, 1], "texture": "side", "tint_index": 0 }
}
},
{
"from": [1, 0, 1],
"to": [16, 2, 15],
"faces": {
"north": { "uv": [0, 0, 1, 16], "texture": "top", "tint_index": 0 },
"south": { "uv": [0, 0, 1, 16], "texture": "top", "tint_index": 0 },
"west": { "uv": [0, 0, 1, 16], "texture": "top", "tint_index": 0 },
"east": { "uv": [0, 0, 1, 16], "texture": "top", "tint_index": 0 },
"up": { "uv": [2, 1, 16, 15], "texture": "top", "tint_index": 0 },
"down": { "uv": [0, 0, 1, 1], "texture": "top", "tint_index": 0 }
}
},
{
"from": [1, 14, 1],
"to": [16, 16, 15],
"faces": {
"north": { "uv": [0, 0, 1, 16], "texture": "top", "tint_index": 0 },
"south": { "uv": [0, 0, 1, 16], "texture": "top", "tint_index": 0 },
"west": { "uv": [0, 0, 1, 16], "texture": "top", "tint_index": 0 },
"east": { "uv": [1, 0, 15, 2], "texture": "top", "tint_index": 0 },
"up": { "uv": [1, 1, 16, 15], "texture": "top", "tint_index": 0 },
"down": { "uv": [0, 0, 1, 1], "texture": "top", "tint_index": 0 }
}
}
]
}

View File

@@ -0,0 +1,12 @@
{
"parent": "minecraft:block/cube",
"textures": {
"up": "neocomputers:block/relay_top",
"down": "neocomputers:block/screen_bottom",
"north": "neocomputers:block/relay_side",
"south": "neocomputers:block/relay_side",
"east": "neocomputers:block/relay_side",
"west": "neocomputers:block/relay_side",
"particle": "neocomputers:block/case_top"
}
}

View File

@@ -0,0 +1,3 @@
{
"parent": "neocomputers:block/rack"
}

View File

@@ -0,0 +1,3 @@
{
"parent": "neocomputers:block/relay"
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "neocomputers:item/server0"
}
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "neocomputers:item/server1"
}
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "neocomputers:item/server2"
}
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "neocomputers:item/server_creative"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

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