Compare commits
149 Commits
e21ee3057a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 97ed29fc23 | |||
| 1c03653fee | |||
| c016cebb0f | |||
| 885f594ca0 | |||
| 45831b780a | |||
| 6b0c4468ec | |||
| d15b34939e | |||
| 3f95944314 | |||
| 21493fec04 | |||
| 42331390e7 | |||
| cdb98bd85e | |||
| 7f58fdf55b | |||
| 17bc614eb9 | |||
| b78cc44d89 | |||
| 4d37483057 | |||
| 4d84ec2ed4 | |||
| 5249832bd6 | |||
| e9885940e2 | |||
| c63912c1f3 | |||
| 323d213291 | |||
| b8968942f8 | |||
| 0eee9f5163 | |||
| 9fd6ea8881 | |||
| c6fc9ed9e9 | |||
| 9ea3727dfb | |||
| 21516ee923 | |||
| d8e79d8719 | |||
| 5dbd0b8734 | |||
| 710547a014 | |||
| 424f77ba10 | |||
| 02c0c5d60c | |||
| 0b33eda8a8 | |||
| cd909dbd80 | |||
| 8fb4921e30 | |||
| 86b3486622 | |||
| 9a733e0a81 | |||
| 740318c020 | |||
| 149cfec3ed | |||
| 4d2cb14e45 | |||
| 7c27955c14 | |||
| 71899c0013 | |||
| ced17282f9 | |||
| a57fca82e5 | |||
| 3b7e2b02b6 | |||
| c968b97faf | |||
| 6ef6826483 | |||
| ba826b47a8 | |||
| 02114fc02a | |||
| 9ac53a0f0e | |||
| 1f1a456f1d | |||
| 43255b1caf | |||
| 464584877c | |||
| 79053b277a | |||
| 80130a68e1 | |||
| fc8a4aaa58 | |||
| cb380880e4 | |||
| 26ec989a4c | |||
| 883833351c | |||
| 400a773a04 | |||
| 6532fea543 | |||
| b39d016feb | |||
| 477abfd80b | |||
| 8edd36124b | |||
| ed6ddddf98 | |||
| 83749bf3ba | |||
| f3280f83d1 | |||
| 88ffaf7a22 | |||
| 8004658a0a | |||
| a8b45c6565 | |||
| ab0a484021 | |||
| f9ddc36611 | |||
| a591c9da6f | |||
| 471921c774 | |||
| cbeb29eeb3 | |||
| 40f3ce9083 | |||
| 3bfc07c392 | |||
| a8ca4b1e35 | |||
| 8ccdb76829 | |||
| cb7be27464 | |||
| 6d20d204a6 | |||
| b6afa13e72 | |||
| 66097fdd6c | |||
| 55f30283c3 | |||
| c98ec2fc99 | |||
| faa1be7175 | |||
| c8da0aba68 | |||
| 35a659475a | |||
| 7542052d1e | |||
|
|
a14d3a2b73 | ||
|
|
f3ebc4f684 | ||
|
|
8b10960612 | ||
|
|
d731a2bc13 | ||
| f2f79244e6 | |||
| 6adce7d3b1 | |||
|
|
00b0014456 | ||
|
|
9de31e8c30 | ||
|
|
dd5d33cb19 | ||
|
|
51a5d4d4ce | ||
|
|
2baaee741f | ||
| f9e33f9f05 | |||
| 0416ddd4a5 | |||
| b025159791 | |||
| f1d39aa12c | |||
| 5c44ee6c86 | |||
| 74d1015bcd | |||
| d3fb4a65cb | |||
| 9d57926432 | |||
| 2c97ab6409 | |||
| 4fb35c5080 | |||
| 62ff4dfa96 | |||
|
|
5ddafd2d84 | ||
| dc16cebc25 | |||
|
|
15b6b2c5ad | ||
| df86086d10 | |||
|
|
07c8dc914d | ||
|
|
d48c7c3035 | ||
|
|
8634bdb4ce | ||
|
|
a53a633b33 | ||
| 7d4ee8593c | |||
| 93a4c4318f | |||
| a352b8f6e8 | |||
| a4533e03bb | |||
| 834a9c7ed8 | |||
| 8dc774e5ab | |||
| 462a6d1bdf | |||
| 61a1c52106 | |||
| 21746422f6 | |||
| ab9d1baa36 | |||
| 8d146fbd17 | |||
| 407e0b44ac | |||
| 94e6c505bd | |||
| ae9a3fd2c7 | |||
| e38184d3a3 | |||
| db2cbbbe8d | |||
| 59358e6b08 | |||
|
|
f456b9b04e | ||
|
|
e6dfc1adc5 | ||
|
|
7adaf4d8df | ||
| 71b6d3a606 | |||
|
|
f5b5a30299 | ||
|
|
3485467ca4 | ||
| fe7bad2b88 | |||
| a687e4d4d4 | |||
| 33d0ae7097 | |||
| 591972d913 | |||
|
|
0d92d59958 | ||
|
|
9af5b77a1f | ||
| dd21f21c2a | |||
| 7f9abfb8ca |
6
.config/lite-xl/init.lua
Normal file
6
.config/lite-xl/init.lua
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
local core = require "core"
|
||||||
|
local config = require "config"
|
||||||
|
|
||||||
|
|
||||||
|
config.ignore_files = { "^build$", "^buildSrc$", "^gradle$", "^\.kotlin$", "^\.idea$", "^\.vscode$", "^\.gradle$", "^\.architectury-transformer$" }
|
||||||
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -453,3 +453,9 @@ FodyWeavers.xsd
|
|||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
/.kotlin/
|
/.kotlin/
|
||||||
|
|
||||||
|
|
||||||
|
/versions/*/src
|
||||||
|
|
||||||
|
.lite_project.lua
|
||||||
|
.config/lite-xl/**
|
||||||
37
.lite_project.lua
Normal file
37
.lite_project.lua
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
-- Put project's module settings here.
|
||||||
|
-- This module will be loaded when opening a project, after the user module
|
||||||
|
-- configuration.
|
||||||
|
-- It will be automatically reloaded when saved.
|
||||||
|
|
||||||
|
local config = require "core.config"
|
||||||
|
|
||||||
|
config.ignore_files = {"^buildSrc$", "^gradle$", "\\.kotlin", "\\.idea", "\\.vscode", "\\.gradle", "\\.architectury-transformer" }
|
||||||
|
|
||||||
|
|
||||||
|
-- you can add some patterns to ignore files within the project
|
||||||
|
-- config.ignore_files = {"^%.", <some-patterns>}
|
||||||
|
|
||||||
|
-- Patterns are normally applied to the file's or directory's name, without
|
||||||
|
-- its path. See below about how to apply filters on a path.
|
||||||
|
--
|
||||||
|
-- Here some examples:
|
||||||
|
--
|
||||||
|
-- "^%." matches any file of directory whose basename begins with a dot.
|
||||||
|
--
|
||||||
|
-- When there is an '/' or a '/$' at the end, the pattern will only match
|
||||||
|
-- directories. When using such a pattern a final '/' will be added to the name
|
||||||
|
-- of any directory entry before checking if it matches.
|
||||||
|
--
|
||||||
|
-- "^%.git/" matches any directory named ".git" anywhere in the project.
|
||||||
|
--
|
||||||
|
-- If a "/" appears anywhere in the pattern (except when it appears at the end or
|
||||||
|
-- is immediately followed by a '$'), then the pattern will be applied to the full
|
||||||
|
-- path of the file or directory. An initial "/" will be prepended to the file's
|
||||||
|
-- or directory's path to indicate the project's root.
|
||||||
|
--
|
||||||
|
-- "^/node_modules/" will match a directory named "node_modules" at the project's root.
|
||||||
|
-- "^/build.*/" will match any top level directory whose name begins with "build".
|
||||||
|
-- "^/subprojects/.+/" will match any directory inside a top-level folder named "subprojects".
|
||||||
|
|
||||||
|
-- You may activate some plugins on a per-project basis to override the user's settings.
|
||||||
|
-- config.plugins.trimwitespace = true
|
||||||
@@ -8,8 +8,7 @@ the mod for that version!
|
|||||||
Also, try reading about how stonecutter's conditional macros work (those can be seen as the `//?` statements in the code).
|
Also, try reading about how stonecutter's conditional macros work (those can be seen as the `//?` statements in the code).
|
||||||
Stonecutter automatically comments and uncomments them when you switch between versions or loaders, you shouldn't do it yourself.
|
Stonecutter automatically comments and uncomments them when you switch between versions or loaders, you shouldn't do it yourself.
|
||||||
|
|
||||||
The minecraft version this mod is currently being developed on is 1.21.11 neoforge or fabric. Although the project stonecutter.gradle.kts
|
The minecraft version this mod is currently being developed on is 1.21.11 neoforge or fabric.
|
||||||
is currently using 1.21.9-fabric, you can easily change it with the gradle task.
|
|
||||||
|
|
||||||
The recommended IDE for this is IntelliJ IDEA 2026.1, and the JDK used is Eclipse Temurin 25.0.2 (from Adoptium), although
|
The recommended IDE for this is IntelliJ IDEA 2026.1, and the JDK used is Eclipse Temurin 25.0.2 (from Adoptium), although
|
||||||
you should be able to use any other build of OpenJDK 26.
|
you should be able to use any other build of OpenJDK 26.
|
||||||
35
TODO.md
Normal file
35
TODO.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Networking
|
||||||
|
> All that is left is optimization
|
||||||
|
|
||||||
|
## Mergeable device nodes
|
||||||
|
|
||||||
|
For optimization of screen grouping and cables, we should add a system that allows merging nodes
|
||||||
|
|
||||||
|
## Copy the networking optimizations of OC
|
||||||
|
> https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/src/main/scala/li/cil/oc/server/network/Network.scala
|
||||||
|
|
||||||
|
We should implement this, as it not only makes behavior match better, but also optimizes
|
||||||
|
it a lot. We do need to replace the NEIGHBORS rule with implementing the SOME visibility.
|
||||||
|
A direct port is not needed, just taking heavy inspiration from OC.
|
||||||
|
|
||||||
|
## Optimize power balancing
|
||||||
|
|
||||||
|
Use a smarter algorithm to prevent N storage nodes from iterating N^2 nodes each tick
|
||||||
|
to balance power.
|
||||||
|
|
||||||
|
# Computation
|
||||||
|
> Pretty important for a computer mod
|
||||||
|
|
||||||
|
## JNI
|
||||||
|
|
||||||
|
We need the JNI system so we can salvage our hard labor thrown into NeoNucleus.
|
||||||
|
Also because it is a capable engine and has a good API for architectures, and NCL is very capable.
|
||||||
|
|
||||||
|
## Worker threads
|
||||||
|
|
||||||
|
Computers need worker threads for running non-synchronized code, because otherwise we're cooked
|
||||||
|
|
||||||
|
## Entities as machines
|
||||||
|
|
||||||
|
Aside from blocks like cases and robots, we should also support entities like drones.
|
||||||
|
Not only for OC parity, but also as addons would def love that.
|
||||||
@@ -11,6 +11,15 @@ plugins {
|
|||||||
val minecraft = stonecutter.current.version
|
val minecraft = stonecutter.current.version
|
||||||
val loader = loom.platform.get().name.lowercase()
|
val loader = loom.platform.get().name.lowercase()
|
||||||
|
|
||||||
|
fun minecraftVersionToNum(ver: String): Int {
|
||||||
|
val parts = ver.split(".")
|
||||||
|
if(parts.size == 2) {
|
||||||
|
// 26.1 and above
|
||||||
|
return parts[0].toInt() * 10000 + parts[1].toInt()
|
||||||
|
}
|
||||||
|
return parts[1].toInt() * 100 + parts[2].toInt()
|
||||||
|
}
|
||||||
|
|
||||||
version = "${mod.version}+$minecraft"
|
version = "${mod.version}+$minecraft"
|
||||||
group = mod.group
|
group = mod.group
|
||||||
base {
|
base {
|
||||||
@@ -21,6 +30,7 @@ architectury.common(stonecutter.tree.branches.mapNotNull {
|
|||||||
if (stonecutter.current.project !in it) null
|
if (stonecutter.current.project !in it) null
|
||||||
else it.prop("loom.platform")
|
else it.prop("loom.platform")
|
||||||
})
|
})
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven("https://maven.neoforged.net/releases/")
|
maven("https://maven.neoforged.net/releases/")
|
||||||
|
|
||||||
@@ -32,12 +42,41 @@ repositories {
|
|||||||
name = "Kotlin for Forge"
|
name = "Kotlin for Forge"
|
||||||
setUrl("https://thedarkcolour.github.io/KotlinForForge/")
|
setUrl("https://thedarkcolour.github.io/KotlinForForge/")
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
name = "ParchmentMC"
|
||||||
|
setUrl("https://maven.parchmentmc.org")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
minecraft("com.mojang:minecraft:$minecraft")
|
minecraft("com.mojang:minecraft:$minecraft")
|
||||||
mappings(loom.officialMojangMappings())
|
// mappings(loom.officialMojangMappings())
|
||||||
|
|
||||||
modApi("dev.architectury:architectury:${mod.dep("architectury_version")}")
|
mappings(loom.layered(){
|
||||||
|
var date = ""
|
||||||
|
when (minecraft) {
|
||||||
|
"1.20.1" -> date = "2023.09.03"
|
||||||
|
"1.20.4" -> date = "2024.04.14"
|
||||||
|
"1.21.1" -> date = "2024.11.17"
|
||||||
|
"1.21.9" -> date = "2025.10.05"
|
||||||
|
"1.21.11" -> date = "2025.12.20"
|
||||||
|
else -> date="idk lol 67"
|
||||||
|
}
|
||||||
|
|
||||||
|
officialMojangMappings() // TODO: versions
|
||||||
|
parchment("org.parchmentmc.data:parchment-${minecraft}:${date}@zip")
|
||||||
|
})
|
||||||
|
var archversion = "idk"
|
||||||
|
when(minecraft) { // NOTE: add more entries if you want to add more versions
|
||||||
|
"1.20.1" -> archversion = "9.2.14"
|
||||||
|
"1.20.4" -> archversion = "11.1.17"
|
||||||
|
"1.21.1" -> archversion = "13.0.8"
|
||||||
|
"1.21.9" -> archversion = "18.0.5"
|
||||||
|
else -> archversion = "19.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// modImplementation("dev.architectury:architectury:$rootProject.architectury_api_version")
|
||||||
|
// modImplementation("dev.architectury:architectury:${archversion}")
|
||||||
|
modImplementation("dev.architectury:architectury:${archversion}")
|
||||||
if (loader == "fabric") {
|
if (loader == "fabric") {
|
||||||
modImplementation("net.fabricmc:fabric-loader:${mod.dep("fabric_loader")}")
|
modImplementation("net.fabricmc:fabric-loader:${mod.dep("fabric_loader")}")
|
||||||
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||||
@@ -45,16 +84,26 @@ dependencies {
|
|||||||
//some features (like automatic resource loading from non vanilla namespaces) work only with fabric API installed
|
//some features (like automatic resource loading from non vanilla namespaces) work only with fabric API installed
|
||||||
//for example translations from assets/modid/lang/en_us.json won't be working, same stuff with textures
|
//for example translations from assets/modid/lang/en_us.json won't be working, same stuff with textures
|
||||||
//but we keep runtime only to not accidentally depend on fabric's api, because it doesn't exist in neo/forge
|
//but we keep runtime only to not accidentally depend on fabric's api, because it doesn't exist in neo/forge
|
||||||
modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${mod.dep("fabric_version")}")
|
//modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${mod.dep("fabric_version")}")
|
||||||
|
modImplementation("net.fabricmc.fabric-api:fabric-api:${mod.dep("fabric_version")}")
|
||||||
modImplementation("net.fabricmc:fabric-language-kotlin:1.13.10+kotlin.2.3.20")
|
modImplementation("net.fabricmc:fabric-language-kotlin:1.13.10+kotlin.2.3.20")
|
||||||
modApi("dev.architectury:architectury-fabric:${mod.dep("architectury_version")}")
|
modApi("dev.architectury:architectury-fabric:${archversion}")
|
||||||
|
fun getTeamRebornEnergy(): String {
|
||||||
|
val curVer = minecraftVersionToNum(minecraft)
|
||||||
|
if (curVer < minecraftVersionToNum("1.20.5")) { return "3.0.0" }
|
||||||
|
if (curVer < minecraftVersionToNum("1.21.0")) { return "4.0.0" }
|
||||||
|
if (curVer < minecraftVersionToNum("1.21.5")) { return "4.1.0" }
|
||||||
|
if (curVer < minecraftVersionToNum("26.1")) { return "4.2.0" }
|
||||||
|
return "5.0.0"
|
||||||
|
}
|
||||||
|
modApi("teamreborn:energy:${getTeamRebornEnergy()}")
|
||||||
}
|
}
|
||||||
if (loader == "forge") {
|
if (loader == "forge") {
|
||||||
"forge"("net.minecraftforge:forge:${minecraft}-${mod.dep("forge_loader")}")
|
"forge"("net.minecraftforge:forge:${minecraft}-${mod.dep("forge_loader")}")
|
||||||
//implementation("thedarkcolour:kotlinforforge:1.16.0")
|
//implementation("thedarkcolour:kotlinforforge:1.16.0")
|
||||||
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||||
|
|
||||||
modApi("dev.architectury:architectury-forge:${mod.dep("architectury_version")}")
|
modApi("dev.architectury:architectury-forge:${archversion}")
|
||||||
"io.github.llamalad7:mixinextras-forge:${mod.dep("mixin_extras")}".let {
|
"io.github.llamalad7:mixinextras-forge:${mod.dep("mixin_extras")}".let {
|
||||||
implementation(it)
|
implementation(it)
|
||||||
include(it)
|
include(it)
|
||||||
@@ -69,8 +118,10 @@ dependencies {
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
|
|
||||||
modApi("dev.architectury:architectury-forge:${mod.dep("architectury_version")}")
|
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
|
||||||
implementation("thedarkcolour:kotlinforforge-neoforge:6.0.0")
|
implementation("thedarkcolour:kotlinforforge-neoforge:6.0.0")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buildscript {
|
buildscript {
|
||||||
@@ -225,3 +276,14 @@ tasks.build {
|
|||||||
group = "versioned"
|
group = "versioned"
|
||||||
description = "Must run through 'chiseledBuild'"
|
description = "Must run through 'chiseledBuild'"
|
||||||
}
|
}
|
||||||
|
if (loader == "fabric") {
|
||||||
|
fabricApi {
|
||||||
|
if (minecraftVersionToNum(minecraft) > minecraftVersionToNum("1.21.4")) {
|
||||||
|
configureDataGeneration() {
|
||||||
|
client = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configureDataGeneration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ mod.mc_targets=[VERSIONED]
|
|||||||
deps.mixin_extras=0.4.1
|
deps.mixin_extras=0.4.1
|
||||||
deps.fabric_loader=0.18.3
|
deps.fabric_loader=0.18.3
|
||||||
deps.fabric_version=[VERSIONED]
|
deps.fabric_version=[VERSIONED]
|
||||||
deps.architectury_version=9.1.12
|
|
||||||
|
|
||||||
deps.forge_loader=[VERSIONED]
|
deps.forge_loader=[VERSIONED]
|
||||||
deps.neoforge_loader=[VERSIONED]
|
deps.neoforge_loader=[VERSIONED]
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
310
gradlew
vendored
Normal file → Executable file
310
gradlew
vendored
Normal file → Executable file
@@ -1,78 +1,128 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# This is normally unused
|
||||||
APP_BASE_NAME=`basename "$0"`
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
DEFAULT_JVM_OPTS=""
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -81,96 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
|
||||||
if $cygwin ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=$((i+1))
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
(0) set -- ;;
|
|
||||||
(1) set -- "$args0" ;;
|
|
||||||
(2) set -- "$args0" "$args1" ;;
|
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=$(save "$@")
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $JAVACMD --add-opens java.base/java.lang=ALL-UNNAMED -version ; then
|
if "$cygwin" || "$msys" ; then
|
||||||
DEFAULT_JVM_OPTS="--add-opens java.base/java.lang=ALL-UNNAMED $DEFAULT_JVM_OPTS"
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
|
||||||
|
|
||||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
cd "$(dirname "$0")"
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
79
gradlew.bat
vendored
79
gradlew.bat
vendored
@@ -1,4 +1,22 @@
|
|||||||
@if "%DEBUG%" == "" @echo off
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -9,25 +27,29 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS=
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -35,48 +57,35 @@ goto fail
|
|||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ stonecutter {
|
|||||||
}
|
}
|
||||||
//i would recommend to use neoforge for mc > 1.20.1, i haven't tested neocomputers for forge on versions higher than that
|
//i would recommend to use neoforge for mc > 1.20.1, i haven't tested neocomputers for forge on versions higher than that
|
||||||
//mc("fabric","1.20.1","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("fabric","1.20.1","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("fabric", "1.20.1", "1.20.4", "1.21.9", "1.21.11")
|
mc("fabric", "1.20.1", "1.20.4", "1.21.1", "1.21.9", "1.21.11")
|
||||||
mc("forge", "1.20.1")
|
mc("forge", "1.20.1")
|
||||||
//WARNING: neoforge uses mods.toml instead of neoforge.mods.toml for versions 1.20.4 (?) and earlier
|
//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.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")
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.neoflock.neocomputers.platforms.fabric.client;
|
||||||
|
|
||||||
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
||||||
|
import net.minecraft.client.color.item.ItemColor;
|
||||||
|
import net.minecraft.client.color.item.ItemColors;
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import org.neoflock.neocomputers.NeoComputers;
|
||||||
|
import org.neoflock.neocomputers.block.Blocks;
|
||||||
|
import org.neoflock.neocomputers.block.CableBlock;
|
||||||
|
import org.neoflock.neocomputers.entity.BlockEntities;
|
||||||
|
import org.neoflock.neocomputers.entity.render.*;
|
||||||
|
import org.neoflock.neocomputers.item.Items;
|
||||||
|
import org.neoflock.neocomputers.platforms.fabric.client.model.ModelLoader;
|
||||||
|
|
||||||
|
public class NeoComputersFabricClient implements ClientModInitializer {
|
||||||
|
@Override
|
||||||
|
public void onInitializeClient() {
|
||||||
|
ModelLoadingPlugin.register(new ModelLoader());
|
||||||
|
BlockEntityRenderers.register(BlockEntities.INSTANCE.getSCREEN_ENTITY().get(), ScreenEntityRenderer::new); // TODO: put this in common
|
||||||
|
BlockEntityRenderers.register(BlockEntities.INSTANCE.getCASE_ENTITY().get(), CaseEntityRenderer::new);
|
||||||
|
BlockEntityRenderers.register(BlockEntities.INSTANCE.getRELAY_ENTITY().get(), RelayEntityRenderer::new);
|
||||||
|
BlockEntityRenderers.register(BlockEntities.INSTANCE.getROBOT_ENTITY().get(), RobotEntityRenderer::new);
|
||||||
|
BlockEntityRenderers.register(BlockEntities.INSTANCE.getRACK_ENTITY().get(), RackEntityRenderer::new);
|
||||||
|
|
||||||
|
ColorProviderRegistry.BLOCK.register((state, world, pos, index) -> {
|
||||||
|
if (index == 0) {
|
||||||
|
return state.getValue(CableBlock.Companion.getCOLOR()).getTextureDiffuseColor();
|
||||||
|
} else {
|
||||||
|
return 0xFFFFFF;
|
||||||
|
}
|
||||||
|
}, Blocks.INSTANCE.getCABLE_BLOCK().get());
|
||||||
|
|
||||||
|
ColorProviderRegistry.ITEM.register((stack, index)-> {
|
||||||
|
return DyeColor.LIGHT_GRAY.getTextureDiffuseColor();
|
||||||
|
}, Items.INSTANCE.getITEMS().getRegistrar().get(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "cable")));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.neoflock.neocomputers.platforms.fabric.client.model;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.neoflock.neocomputers.NeoComputers;
|
||||||
|
import org.neoflock.neocomputers.block.model.RobotModel;
|
||||||
|
|
||||||
|
public class ModelLoader implements ModelLoadingPlugin {
|
||||||
|
public static final ResourceLocation ROBOT = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "robot");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitializeModelLoader(Context pluginContext) {
|
||||||
|
pluginContext.modifyModelOnLoad().register((original, context) -> {
|
||||||
|
final ModelResourceLocation id = context.topLevelId();
|
||||||
|
if (id != null && id.id().equals(ROBOT)) {
|
||||||
|
return new FabricModelWrapper(new RobotModel());
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
//? if neoforge {
|
//? if neoforge {
|
||||||
/*package org.neoflock.neocomputers.neocomputers.platforms.neoforge;
|
/*package org.neoflock.neocomputers.platforms.neoforge;
|
||||||
|
|
||||||
import org.neoflock.neocomputers.ConfigScreen;
|
import org.neoflock.neocomputers.ConfigScreen;
|
||||||
import org.neoflock.neocomputers.NeoComputersInit;
|
|
||||||
import org.neoflock.neocomputers.ModPlatform;
|
import org.neoflock.neocomputers.ModPlatform;
|
||||||
import net.neoforged.fml.ModList;
|
import net.neoforged.fml.ModList;
|
||||||
import net.neoforged.fml.ModLoadingContext;
|
import net.neoforged.fml.ModLoadingContext;
|
||||||
|
|||||||
@@ -1,19 +1,164 @@
|
|||||||
package org.neoflock.neocomputers
|
package org.neoflock.neocomputers
|
||||||
|
|
||||||
|
import dev.architectury.event.events.client.ClientLifecycleEvent
|
||||||
|
import dev.architectury.event.events.common.LifecycleEvent
|
||||||
|
import dev.architectury.event.events.common.PlayerEvent
|
||||||
|
import dev.architectury.event.events.common.TickEvent
|
||||||
|
import dev.architectury.networking.NetworkManager
|
||||||
|
import dev.architectury.platform.Platform
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import org.neoflock.neocomputers.block.Blocks
|
||||||
|
import org.neoflock.neocomputers.entity.BlockEntities
|
||||||
|
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.server.level.ServerPlayer
|
||||||
|
import org.neoflock.neocomputers.block.DeviceBlockEntity
|
||||||
|
import org.neoflock.neocomputers.gui.render.ScreenRenderer
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
import org.neoflock.neocomputers.item.Items
|
||||||
|
import org.neoflock.neocomputers.item.Tabs
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
import org.neoflock.neocomputers.network.Networking
|
||||||
|
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||||
|
import org.neoflock.neocomputers.sounds.Sounds
|
||||||
|
import org.neoflock.neocomputers.utils.FontProvider
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
|
||||||
object NeoComputers {
|
object NeoComputers {
|
||||||
const val MODID: String = "neocomputers"
|
const val MODID: String = "neocomputers"
|
||||||
val LOGGER: Logger = LoggerFactory.getLogger("NeoComputers")
|
val LOGGER: Logger = LoggerFactory.getLogger("NeoComputers")
|
||||||
var PLATFORM: ModPlatform? = null
|
var PLATFORM: ModPlatform? = null
|
||||||
|
|
||||||
|
// val BlockEntityRenderType: RenderType = RenderType.create(
|
||||||
|
// "nc_blockentities",
|
||||||
|
// DefaultVertexFormat.POSITION_TEX,
|
||||||
|
// VertexFormat.Mode.QUADS,
|
||||||
|
// 0xc000, RenderType.CompositeState.builder().setShaderState(RenderStateShard.POSITION_TEX_SHADER).createCompositeState(false)) // TODO: figure out correct buffer size and composite state
|
||||||
|
|
||||||
|
|
||||||
fun entrypoint(platform: ModPlatform?) {
|
fun entrypoint(platform: ModPlatform?) {
|
||||||
PLATFORM = platform
|
PLATFORM = platform
|
||||||
|
|
||||||
|
Blocks.BLOCKS.register()
|
||||||
|
Blocks.registerBlockItems()
|
||||||
|
Items.ITEMS.register()
|
||||||
|
BlockEntities.BLOCKENTITIES.register()
|
||||||
|
BlockEntities.registerPowerBlocks()
|
||||||
|
Menus.MENUS.register()
|
||||||
|
Tabs.TABS.register()
|
||||||
|
Sounds.SOUNDS.register()
|
||||||
|
ComponentRoles.mapDefaultTextures()
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
ClientLifecycleEvent.CLIENT_STARTED.register {
|
||||||
|
FontProvider.load(ResourceLocation.fromNamespaceAndPath(MODID, "font/unscii.hex"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientLifecycleEvent.CLIENT_STOPPING.register {
|
||||||
|
ScreenRenderer.cleanUnboundTex()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
TickEvent.SERVER_POST.register {
|
||||||
|
Networking.tickAllNodes()
|
||||||
|
NodeSynchronizer.syncScreens()
|
||||||
|
}
|
||||||
|
|
||||||
|
TickEvent.PLAYER_POST.register {
|
||||||
|
Sounds.tickCustomSounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
LifecycleEvent.SERVER_STARTING.register {
|
||||||
|
Networking.allNodes.remove()
|
||||||
|
Networking.wirelessNodes.remove()
|
||||||
|
Networking.channels.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerEvent.CLOSE_MENU.register {
|
||||||
|
player, menu ->
|
||||||
|
if(player is ServerPlayer) NodeSynchronizer.playerScreenClosed(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerEvent.PLAYER_QUIT.register {
|
||||||
|
player ->
|
||||||
|
NodeSynchronizer.playerScreenClosed(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
// networking has no way to define a C2S packet type, so we need the listener on both
|
||||||
|
// however, defining it separately on both breaks both ends
|
||||||
|
// so we define it once, but on both platforms
|
||||||
|
if(Platform.getEnvironment() == Env.CLIENT || Platform.getEnvironment() == Env.SERVER) {
|
||||||
|
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, {
|
||||||
|
packet, ctx ->
|
||||||
|
val player = ctx.player
|
||||||
|
if(player is ServerPlayer) {
|
||||||
|
val ent = NodeSynchronizer.screenMap[player]
|
||||||
|
if(ent is DeviceNode) {
|
||||||
|
ent.processScreenInteraction(player, packet.buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.DeviceBlockStateRequest.TYPE, NodeSynchronizer.DeviceBlockStateRequest.CODEC, {
|
||||||
|
packet, ctx ->
|
||||||
|
val player = ctx.player
|
||||||
|
val level = player.level()
|
||||||
|
val dist = packet.blockPos.center.distanceTo(player.position())
|
||||||
|
if(player is ServerPlayer && dist <= NodeSynchronizer.MAX_STATE_DISTANCE_ALLOWED) {
|
||||||
|
val ent = level.getBlockEntity(packet.blockPos)
|
||||||
|
if(ent is DeviceBlockEntity) {
|
||||||
|
ent.sendStateToPlayer(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have to do this because the datagen task runs in the physical server
|
||||||
|
EnvExecutor.runInEnv(Env.CLIENT) {{
|
||||||
|
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC, {
|
||||||
|
packet, ctx ->
|
||||||
|
val level = ctx.player.level()
|
||||||
|
val ent = level.getBlockEntity(packet.blockPos)
|
||||||
|
if(ent is DeviceBlockEntity) {
|
||||||
|
ent.processCommits(packet.buffers)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC, {
|
||||||
|
packet, ctx ->
|
||||||
|
val scr = Minecraft.getInstance().screen
|
||||||
|
if(scr is GenericContainerScreen<*>) {
|
||||||
|
scr.processScreenStatePacket(packet.buffer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC, {
|
||||||
|
packet, ctx ->
|
||||||
|
// TODO: implement volume
|
||||||
|
Sounds.beep(packet.pos.center, packet.pattern, packet.freq, packet.duration.toMillis().toInt())
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
EnvExecutor.runInEnv(Env.SERVER) {{
|
||||||
|
// https://github.com/architectury/architectury-api/issues/518
|
||||||
|
NetworkManager.registerS2CPayloadType(NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC)
|
||||||
|
NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC)
|
||||||
|
NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC)
|
||||||
|
}}
|
||||||
|
|
||||||
|
LOGGER.info("Registered!")
|
||||||
//LOGGER.info("Started mod in %s loader".formatted(NeoComputersInit.PLATFORM.getModloader()))
|
//LOGGER.info("Started mod in %s loader".formatted(NeoComputersInit.PLATFORM.getModloader()))
|
||||||
//LOGGER.info("Kotlin: %s".formatted(NeoComputers.hello()))
|
//LOGGER.info("Kotlin: %s".formatted(NeoComputers.hello()))
|
||||||
LOGGER.info("Started mod in ${NeoComputers.PLATFORM?.modloader} loader")
|
LOGGER.info("Started mod in ${PLATFORM?.modloader} loader")
|
||||||
LOGGER.info("Hello from kotlin!")
|
LOGGER.info("Hello from kotlin!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
package org.neoflock.neocomputers.block
|
package org.neoflock.neocomputers.block
|
||||||
|
|
||||||
|
import dev.architectury.registry.registries.RegistrySupplier
|
||||||
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.resources.ResourceKey
|
||||||
import net.minecraft.world.level.block.Block
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import com.google.common.base.Suppliers
|
||||||
|
|
||||||
class BaseBlock : Block {
|
open class BaseBlock(properties: Properties = Properties.of()) : Block(properties) { // TODO: create a TieredBaseBlock class that extends this or something
|
||||||
protected val tier: Int
|
// val tier: Int
|
||||||
constructor(tier: Int): super(Properties.of()) {
|
|
||||||
this.tier = tier
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun getTier(): Int {
|
companion object Registry {
|
||||||
return tier
|
fun register(name: String, sup: Supplier<BaseBlock>): RegistrySupplier<Block> = Blocks.BLOCKS.register(name, sup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,50 +1,48 @@
|
|||||||
package org.libreflock.neocomputers.block
|
package org.neoflock.neocomputers.block
|
||||||
|
|
||||||
|
import com.google.common.base.Suppliers
|
||||||
import dev.architectury.registry.registries.DeferredRegister
|
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 dev.architectury.registry.registries.RegistrySupplier
|
||||||
import net.minecraft.core.registries.Registries
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.resources.ResourceKey
|
||||||
import net.minecraft.world.item.BlockItem
|
import net.minecraft.world.item.BlockItem
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.level.block.Block
|
import net.minecraft.world.level.block.Block
|
||||||
import org.libreflock.neocomputers.item.Items
|
import net.minecraft.world.level.block.state.BlockBehaviour
|
||||||
import org.libreflock.neocomputers.item.Tabs
|
// import org.neoflock.neocomputers.item.Tabs
|
||||||
|
// import org.neoflock.neocomputers.item.Items
|
||||||
import org.neoflock.neocomputers.NeoComputers
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
import org.neoflock.neocomputers.block.BaseBlock
|
import org.neoflock.neocomputers.block.BaseBlock
|
||||||
|
import org.neoflock.neocomputers.item.Items
|
||||||
|
import org.neoflock.neocomputers.item.Tabs
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
object Blocks {
|
object Blocks {
|
||||||
val BLOCKS: DeferredRegister<Block?> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK)
|
|
||||||
|
|
||||||
|
|
||||||
val CASE: MutableList<RegistrySupplier<Block?>?>? =
|
val BLOCKS: DeferredRegister<Block> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK)
|
||||||
BaseBlock.register(intArrayOf(0, 1, 2), "case", { tier -> CaseBlock(tier) })
|
val SCREEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("screen") { ScreenBlock() }
|
||||||
|
val CAPACITOR_BLOCK: RegistrySupplier<Block> = BaseBlock.register("capacitor") { CapacitorBlock(1) }
|
||||||
// public static final RegistrySupplier<Block> CASE0 = BLOCKS.register("case0", () -> new CaseBlock(0));
|
val CAPACITOR_BLOCK2: RegistrySupplier<Block> = BaseBlock.register("capacitor2") { CapacitorBlock(2) }
|
||||||
// public static final RegistrySupplier<Block> CASE1 = BLOCKS.register("case1", () -> new CaseBlock(1));
|
val CAPACITOR_BLOCK3: RegistrySupplier<Block> = BaseBlock.register("capacitor3") { CapacitorBlock(3) }
|
||||||
// public static final RegistrySupplier<Block> CASE2 = BLOCKS.register("case2", () -> new CaseBlock(2));
|
val SOLARGEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("solargen") { SolarGeneratorBlock() }
|
||||||
// public static final RegistrySupplier<Block> CABLE = BLOCKS.register("cable", () -> new CableBlock());
|
val COMBUSTGEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("combustgen") { CombustionGeneratorBlock() }
|
||||||
val SCREEN: RegistrySupplier<Block?>? = BLOCKS.register<Block?>("screen", Supplier { ScreenBlock() })
|
val CASE_BLOCK: RegistrySupplier<Block> = BaseBlock.register("case") { CaseBlock() }
|
||||||
val CABLE: RegistrySupplier<Block?>? = BLOCKS.register<Block?>("cable", Supplier { CableBlock() })
|
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() }
|
||||||
|
|
||||||
fun registerBlockItems() {
|
fun registerBlockItems() {
|
||||||
BLOCKS.forEach(Consumer { sup: RegistrySupplier<Block?>? ->
|
BLOCKS.forEach(Consumer { sup: RegistrySupplier<Block> ->
|
||||||
// sup.pre
|
NeoComputers.LOGGER.info(sup.id.toString())
|
||||||
// sup.((blk) -> {
|
val id = ResourceKey.create(Registries.ITEM, sup.id)
|
||||||
// NeoComputers.LOGGER.info(blk.getDescriptionId());
|
Items.ITEMS.register(sup.id.path) { BlockItem(sup.get()!!, Item.Properties().`arch$tab`(Tabs.TAB))}
|
||||||
// if (blk instanceof BaseBlock) {
|
|
||||||
// Items.ITEMS.register(sup.getId().getPath(), () -> new BaseBlock.BaseBlockItem(blk, new Item.Properties().arch$tab(Tabs.TAB)));
|
|
||||||
// } else {
|
|
||||||
// Items.ITEMS.register(sup.getId().getPath(), () -> new BlockItem(blk, new Item.Properties().arch$tab(Tabs.TAB)));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
Items.ITEMS.register(sup!!.getId().getPath(), {
|
|
||||||
if (sup.get() is BaseBlock) {
|
|
||||||
return@register BaseBlockItem(sup.get(), Item.Properties().`arch$tab`(Tabs.TAB))
|
|
||||||
} else {
|
|
||||||
return@register BlockItem(sup.get(), Item.Properties().`arch$tab`(Tabs.TAB))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
176
src/main/kotlin/org/neoflock/neocomputers/block/CableBlock.kt
Normal file
176
src/main/kotlin/org/neoflock/neocomputers/block/CableBlock.kt
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package org.neoflock.neocomputers.block
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.blockentity.PistonHeadRenderer
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.world.InteractionHand
|
||||||
|
import net.minecraft.world.ItemInteractionResult
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.item.DyeColor
|
||||||
|
import net.minecraft.world.item.DyeItem
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.item.Items
|
||||||
|
import net.minecraft.world.level.BlockGetter
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.EntityBlock
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty
|
||||||
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
|
import net.minecraft.world.phys.shapes.BooleanOp
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.entity.CableEntity
|
||||||
|
|
||||||
|
class CableBlock() : DeviceBlock(Properties.of()), EntityBlock {
|
||||||
|
companion object {
|
||||||
|
val NORTH = BooleanProperty.create("north")
|
||||||
|
val EAST = BooleanProperty.create("east")
|
||||||
|
val WEST = BooleanProperty.create("west")
|
||||||
|
val SOUTH = BooleanProperty.create("south")
|
||||||
|
val UP = BooleanProperty.create("up")
|
||||||
|
val DOWN = BooleanProperty.create("down")
|
||||||
|
|
||||||
|
val COLOR = EnumProperty<DyeColor>.create("color", DyeColor::class.java)
|
||||||
|
|
||||||
|
val MIN = 0.375
|
||||||
|
val MAX = 1-MIN
|
||||||
|
|
||||||
|
val shapeCache: Array<VoxelShape?> = arrayOfNulls(Direction.entries.size*Direction.entries.size*Direction.entries.size*Direction.entries.size*Direction.entries.size*Direction.entries.size)
|
||||||
|
|
||||||
|
fun calcIdx(north: Boolean, south: Boolean, east: Boolean, west: Boolean, up: Boolean, down: Boolean): Int {
|
||||||
|
var idx = if (down) 1 else 0
|
||||||
|
idx += 2*(if (up) 1 else 0)
|
||||||
|
idx += 4*(if (west) 1 else 0)
|
||||||
|
idx += 8*(if (east) 1 else 0)
|
||||||
|
idx += 16*(if (south) 1 else 0)
|
||||||
|
idx += 32*(if (north) 1 else 0)
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeShapes() {
|
||||||
|
NeoComputers.LOGGER.info("[CABLE] recomputing shapes")
|
||||||
|
for (north in arrayOf(false, true)) { // shut up
|
||||||
|
for (south in arrayOf(false, true)) {
|
||||||
|
for (east in arrayOf(false, true)) {
|
||||||
|
for (west in arrayOf(false, true)) {
|
||||||
|
for (up in arrayOf(false, true)) {
|
||||||
|
for (down in arrayOf(false, true)) {
|
||||||
|
val shape = makeShape(north, south, east, west, up, down)
|
||||||
|
val idx = calcIdx(north, south, east,west, up, down)
|
||||||
|
shapeCache[idx] = shape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeShape(north: Boolean, south: Boolean, east: Boolean, west: Boolean, up: Boolean, down: Boolean): VoxelShape {
|
||||||
|
var shape = Shapes.box(MIN, MIN, MIN, MAX, MAX, MAX)
|
||||||
|
if (north) shape = Shapes.join(shape, Shapes.box(MIN, MIN, 0.0, MAX, MAX, MIN ), BooleanOp.OR)
|
||||||
|
if (south) shape = Shapes.join(shape, Shapes.box(MIN, MIN, MAX, MAX, MAX, 1.0 ), BooleanOp.OR)
|
||||||
|
if (east) shape = Shapes.join(shape, Shapes.box(MAX, MIN, MIN, 1.0, MAX, MAX), BooleanOp.OR)
|
||||||
|
if (west) shape = Shapes.join(shape, Shapes.box(0.0, MIN, MIN, MIN, MAX, MAX ), BooleanOp.OR)
|
||||||
|
if (up) shape = Shapes.join(shape, Shapes.box(MIN, MAX, MIN, MAX, 1.0, MAX), BooleanOp.OR)
|
||||||
|
if (down) shape = Shapes.join(shape, Shapes.box(MIN, 0.0, MIN, MAX, MIN, MAX ), BooleanOp.OR)
|
||||||
|
return shape
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPropByDirection(direction: Direction): BooleanProperty {
|
||||||
|
return when (direction) {
|
||||||
|
Direction.NORTH -> NORTH
|
||||||
|
Direction.SOUTH -> SOUTH
|
||||||
|
Direction.WEST -> WEST
|
||||||
|
Direction.EAST -> EAST
|
||||||
|
Direction.UP -> UP
|
||||||
|
Direction.DOWN -> DOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun shouldConnect(pos: BlockPos, npos: BlockPos, level: Level): Boolean {
|
||||||
|
val ent = level.getBlockEntity(npos)
|
||||||
|
val blockState = level.getBlockState(pos)
|
||||||
|
val theirState = level.getBlockState(npos)
|
||||||
|
|
||||||
|
val universal = DyeColor.LIGHT_GRAY
|
||||||
|
if(ent is CableEntity) {
|
||||||
|
val ourColor = blockState.getValue(COLOR)
|
||||||
|
val theirColor = theirState.getValue(COLOR)
|
||||||
|
|
||||||
|
if(ourColor.equals(universal)) return true
|
||||||
|
if(theirColor.equals(universal)) return true
|
||||||
|
if(ourColor.equals(theirColor)) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ent is DeviceBlockEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerDefaultState(stateDefinition.any()
|
||||||
|
.setValue(NORTH, false)
|
||||||
|
.setValue(EAST, false)
|
||||||
|
.setValue(WEST, false)
|
||||||
|
.setValue(SOUTH, false)
|
||||||
|
.setValue(UP, false)
|
||||||
|
.setValue(DOWN, false)
|
||||||
|
.setValue(COLOR, DyeColor.LIGHT_GRAY)
|
||||||
|
)
|
||||||
|
makeShapes()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||||
|
super.createBlockStateDefinition(builder
|
||||||
|
.add(NORTH)
|
||||||
|
.add(EAST)
|
||||||
|
.add(SOUTH)
|
||||||
|
.add(WEST)
|
||||||
|
.add(UP)
|
||||||
|
.add(DOWN)
|
||||||
|
.add(COLOR))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newBlockEntity(pos: BlockPos, state: BlockState) = CableEntity(pos, state)
|
||||||
|
|
||||||
|
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? {
|
||||||
|
val idx = calcIdx(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN))
|
||||||
|
return shapeCache[idx];
|
||||||
|
// return makeShape(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun neighborChanged(state: BlockState, level: Level, pos: BlockPos, neighborBlock: Block, neighborPos: BlockPos, movedByPiston: Boolean) {
|
||||||
|
// val neighbors = getNeighbourEntities(blockPos, level)
|
||||||
|
// for (dir in Direction.entries) {
|
||||||
|
// val ent = level.getBlockEntity(blockPos.relative(dir))
|
||||||
|
// level.setBlockAndUpdate(blockPos, blockState.setValue(getPropByDirection(dir), (ent is NodeBlockEntity || ent is CableEntity)))
|
||||||
|
// }
|
||||||
|
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston)
|
||||||
|
val diff = pos.subtract(neighborPos)
|
||||||
|
val dir = Direction.fromDelta(diff.x, diff.y, diff.z)!!.opposite
|
||||||
|
val ent = level.getBlockEntity(neighborPos)
|
||||||
|
// val value = ent is NodeBlockEntity || (ent is CableEntity && (level.getBlockState(neighborPos).getValue(COLOR).equals(state.getValue(COLOR)) || state.getValue(COLOR).equals(DyeColor.LIGHT_GRAY)))
|
||||||
|
level.setBlockAndUpdate(pos, state.setValue(getPropByDirection(dir), shouldConnect(pos, neighborPos, level)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun useItemOn(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hand: InteractionHand, hitResult: BlockHitResult): ItemInteractionResult? {
|
||||||
|
// return super.useItemOn(stack, state, level, pos, player, hand, hitResult)
|
||||||
|
if (stack.item is DyeItem) {
|
||||||
|
val dyeitem = stack.item as DyeItem
|
||||||
|
level.setBlockAndUpdate(pos, state.setValue(COLOR, dyeitem.dyeColor))
|
||||||
|
val ent = level.getBlockEntity(pos)
|
||||||
|
if(ent is CableEntity) {
|
||||||
|
ent.connectionsAreDirty = true
|
||||||
|
}
|
||||||
|
return ItemInteractionResult.SUCCESS
|
||||||
|
}
|
||||||
|
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt
Normal file
86
src/main/kotlin/org/neoflock/neocomputers/block/Capacitor.kt
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package org.neoflock.neocomputers.block
|
||||||
|
|
||||||
|
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
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
|
import org.neoflock.neocomputers.entity.BlockEntities
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
import org.neoflock.neocomputers.network.PowerRole
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : DeviceBlockEntity(type, pos, state) {
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: cache list
|
||||||
|
override fun getDeviceNodes() = listOf(deviceNode)
|
||||||
|
override fun getNodeFromSide(directionToRequester: Direction) = deviceNode
|
||||||
|
|
||||||
|
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||||
|
super.loadAdditional(tag, registries)
|
||||||
|
deviceNode.energy = min(tag.getLong("energy"), deviceNode.energyCapacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||||
|
super.saveAdditional(compoundTag, provider)
|
||||||
|
compoundTag.putLong("energy", deviceNode.energy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CapacitorEntityTier1(pos: BlockPos, state: BlockState): CapacitorEntity(20000, BlockEntities.CAPACITOR_ENTITY.get(), pos, state)
|
||||||
|
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) : DeviceBlock() {
|
||||||
|
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
|
||||||
|
val cap: CapacitorEntity = when(tier) {
|
||||||
|
1 -> CapacitorEntityTier1(blockPos, blockState)
|
||||||
|
2 -> CapacitorEntityTier2(blockPos, blockState)
|
||||||
|
3 -> CapacitorEntityTier3(blockPos, blockState)
|
||||||
|
else -> throw UnsupportedOperationException("unsupported tier: $tier")
|
||||||
|
}
|
||||||
|
return cap
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun useWithoutItem(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
player: Player,
|
||||||
|
blockHitResult: BlockHitResult
|
||||||
|
): InteractionResult {
|
||||||
|
if(level.isClientSide()) {
|
||||||
|
val ent = level.getBlockEntity(blockPos)
|
||||||
|
if(ent is CapacitorEntity) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
137
src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt
Normal file
137
src/main/kotlin/org/neoflock/neocomputers/block/CaseBlock.kt
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
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.network.NodeSynchronizer
|
||||||
|
import org.neoflock.neocomputers.sounds.Sounds
|
||||||
|
|
||||||
|
class CaseBlock() : DeviceBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance).noOcclusion()) { // placeholder stuff
|
||||||
|
companion object {
|
||||||
|
val FACING: EnumProperty<Direction> = EnumProperty.create<Direction>("facing", Direction::class.java)
|
||||||
|
val COMPUTER_RUNNING = BooleanProperty.create("running")!!
|
||||||
|
|
||||||
|
fun getLuminance(blockState: BlockState): Int {
|
||||||
|
return if(blockState.getValue(COMPUTER_RUNNING)) 3 else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(COMPUTER_RUNNING, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState)
|
||||||
|
|
||||||
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||||
|
builder.add(COMPUTER_RUNNING)
|
||||||
|
builder.add(FACING)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMachine(level: BlockGetter, blockPos: BlockPos): CaseBlockEntity {
|
||||||
|
return level.getBlockEntity(blockPos) as CaseBlockEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSignalSource(state: BlockState): Boolean = true
|
||||||
|
|
||||||
|
override fun getSignal(
|
||||||
|
blockState: BlockState,
|
||||||
|
blockGetter: BlockGetter,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
direction: Direction
|
||||||
|
): Int {
|
||||||
|
return getMachine(blockGetter, blockPos).redstoneOut[direction.opposite.ordinal]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlace(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
blockState2: BlockState,
|
||||||
|
bl: Boolean
|
||||||
|
) {
|
||||||
|
if(!level.isClientSide) {
|
||||||
|
level.updateNeighborsAt(blockPos, this)
|
||||||
|
getMachine(level, blockPos).refetchAllRedstone()
|
||||||
|
}
|
||||||
|
super.onPlace(blockState, level, blockPos, blockState2, bl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||||
|
return defaultBlockState().setValue(FACING, context.horizontalDirection.opposite)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 dir = Direction.getNearest(blockPos2.center.subtract(blockPos.center))
|
||||||
|
getMachine(level, blockPos).refetchRedstone(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun useWithoutItem(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
player: Player,
|
||||||
|
blockHitResult: BlockHitResult
|
||||||
|
): InteractionResult {
|
||||||
|
if(!level.isClientSide) {
|
||||||
|
val ent = level.getBlockEntity(blockPos, BlockEntities.CASE_ENTITY.get()).get()
|
||||||
|
if(player.isCrouching) {
|
||||||
|
// Quickstat
|
||||||
|
ent.start()
|
||||||
|
} else {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
254
src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt
Normal file
254
src/main/kotlin/org/neoflock/neocomputers/block/DeviceBlock.kt
Normal 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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package org.neoflock.neocomputers.block
|
||||||
|
|
||||||
|
import dev.architectury.registry.menu.MenuRegistry
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.particles.ParticleTypes
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import net.minecraft.sounds.SoundEvents
|
||||||
|
import net.minecraft.sounds.SoundSource
|
||||||
|
import net.minecraft.util.RandomSource
|
||||||
|
import net.minecraft.world.Containers
|
||||||
|
import net.minecraft.world.InteractionResult
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.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.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||||
|
import net.minecraft.world.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 : DeviceBlock(), EntityBlock {
|
||||||
|
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make it glow when burning
|
||||||
|
class CombustionGeneratorBlock : DeviceBlock, EntityBlock {
|
||||||
|
companion object {
|
||||||
|
val COMBUSTGEN_ACTIVE = BooleanProperty.create("active")
|
||||||
|
|
||||||
|
fun getLuminance(blockState: BlockState): Int {
|
||||||
|
return if(blockState.getValue(COMBUSTGEN_ACTIVE)) 5 else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(): super(Properties.of().sound(SoundType.STONE).lightLevel(CombustionGeneratorBlock::getLuminance)) {
|
||||||
|
registerDefaultState(defaultBlockState().setValue(COMBUSTGEN_ACTIVE, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = CombustionGeneratorBlockEntity(blockPos, blockState)
|
||||||
|
|
||||||
|
override fun useWithoutItem(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
player: Player,
|
||||||
|
blockHitResult: BlockHitResult
|
||||||
|
): InteractionResult {
|
||||||
|
if(!level.isClientSide()) {
|
||||||
|
val sp = player as ServerPlayer
|
||||||
|
val ent = level.getBlockEntity(blockPos, BlockEntities.COMBUSTGEN_ENTITY.get()).get()
|
||||||
|
NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
|
||||||
|
MenuRegistry.openMenu(sp, ent)
|
||||||
|
}
|
||||||
|
return InteractionResult.SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||||
|
builder.add(COMBUSTGEN_ACTIVE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun animateTick(blockState: BlockState, level: Level, blockPos: BlockPos, randomSource: RandomSource) {
|
||||||
|
if(blockState.getValue(COMBUSTGEN_ACTIVE)) {
|
||||||
|
if(randomSource.nextDouble() < 0.1) level.playLocalSound(blockPos, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.AMBIENT, 1F, 1F, false)
|
||||||
|
|
||||||
|
val x = blockPos.x.toDouble()
|
||||||
|
val y = blockPos.y.toDouble()
|
||||||
|
val z = blockPos.z.toDouble()
|
||||||
|
|
||||||
|
level.addParticle(ParticleTypes.SMOKE, x+0.5, y+1, z+0.5, 0.0, 0.0, 0.0)
|
||||||
|
level.addParticle(ParticleTypes.FLAME, x+0.5, y+1, z+0.5, 0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/main/kotlin/org/neoflock/neocomputers/block/RackBlock.kt
Normal file
69
src/main/kotlin/org/neoflock/neocomputers/block/RackBlock.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
135
src/main/kotlin/org/neoflock/neocomputers/block/RedstoneIO.kt
Normal file
135
src/main/kotlin/org/neoflock/neocomputers/block/RedstoneIO.kt
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package org.neoflock.neocomputers.block
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.world.InteractionResult
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.level.BlockGetter
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.entity.BlockEntities
|
||||||
|
import org.neoflock.neocomputers.network.Networking
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
|
||||||
|
class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.REDSTONEIO_ENTITY.get(), blockPos, blockState) {
|
||||||
|
val redstoneIn = Array<Int>(Direction.entries.size) {0}
|
||||||
|
val redstoneOut = Array<Int>(Direction.entries.size) {0}
|
||||||
|
|
||||||
|
// TODO: have redstone I/O node for component and shi
|
||||||
|
override val deviceNode = object : DeviceNode() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refetch(dir: Direction) {
|
||||||
|
val src = blockPos.offset(dir.stepX, dir.stepY, dir.stepZ)
|
||||||
|
val cur = level?.getSignal(src, dir) ?: 0
|
||||||
|
val idx = dir.ordinal
|
||||||
|
if(redstoneIn[idx] != cur) {
|
||||||
|
onRedstoneSignalChanged(dir, redstoneIn[idx], cur)
|
||||||
|
}
|
||||||
|
redstoneIn[idx] = cur
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refetchAll() {
|
||||||
|
Direction.entries.forEach { refetch(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
|
||||||
|
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(): 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? {
|
||||||
|
val ent = level.getBlockEntity(blockPos)
|
||||||
|
if(ent is RedstoneIOEntity) return ent
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSignalSource(blockState: BlockState): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSignal(
|
||||||
|
blockState: BlockState,
|
||||||
|
blockGetter: BlockGetter,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
direction: Direction
|
||||||
|
): Int {
|
||||||
|
val redstoneIO = getRedstoneIO(blockGetter, blockPos)
|
||||||
|
if(redstoneIO != null) {
|
||||||
|
return redstoneIO.redstoneOut[direction.opposite.ordinal]
|
||||||
|
}
|
||||||
|
return super.getSignal(blockState, blockGetter, blockPos, direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlace(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
blockState2: BlockState,
|
||||||
|
bl: Boolean
|
||||||
|
) {
|
||||||
|
if(!level.isClientSide) {
|
||||||
|
level.updateNeighborsAt(blockPos, this)
|
||||||
|
getRedstoneIO(level, blockPos)?.refetchAll()
|
||||||
|
}
|
||||||
|
super.onPlace(blockState, level, blockPos, blockState2, bl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRemove(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
blockState2: BlockState,
|
||||||
|
bl: Boolean
|
||||||
|
) {
|
||||||
|
if(!level.isClientSide) {
|
||||||
|
level.updateNeighborsAt(blockPos, this)
|
||||||
|
}
|
||||||
|
super.onRemove(blockState, level, blockPos, blockState2, bl)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 dir = Direction.getNearest(blockPos2.center.subtract(blockPos.center))
|
||||||
|
getRedstoneIO(level, blockPos)?.refetch(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun useWithoutItem(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
player: Player,
|
||||||
|
blockHitResult: BlockHitResult
|
||||||
|
): InteractionResult? {
|
||||||
|
if(!level.isClientSide) {
|
||||||
|
val redio = getRedstoneIO(level, blockPos)
|
||||||
|
val dir = blockHitResult.direction
|
||||||
|
if (redio != null) {
|
||||||
|
val idx = dir.ordinal
|
||||||
|
redio.redstoneOut[idx]++
|
||||||
|
redio.redstoneOut[idx] %= 16
|
||||||
|
NeoComputers.LOGGER.info("outputting redstone level ${redio.redstoneOut[idx]} on ${dir.name}")
|
||||||
|
}
|
||||||
|
level.updateNeighborsAt(blockPos, this)
|
||||||
|
}
|
||||||
|
return super.useWithoutItem(blockState, level, blockPos, player, blockHitResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
194
src/main/kotlin/org/neoflock/neocomputers/block/RelayBlock.kt
Normal file
194
src/main/kotlin/org/neoflock/neocomputers/block/RelayBlock.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package org.neoflock.neocomputers.block;
|
||||||
|
|
||||||
|
import dev.architectury.registry.menu.ExtendedMenuProvider
|
||||||
|
import dev.architectury.registry.menu.MenuRegistry
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import net.minecraft.world.InteractionResult
|
||||||
|
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.context.BlockPlaceContext
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty.*
|
||||||
|
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||||
|
import net.minecraft.world.phys.BlockHitResult
|
||||||
|
import org.neoflock.neocomputers.entity.BlockEntities
|
||||||
|
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() : DeviceBlock() {
|
||||||
|
companion object {
|
||||||
|
val FACING_HORIZ: EnumProperty<Direction> = EnumProperty.create<Direction>("facing_horiz", Direction::class.java)
|
||||||
|
val FACING_VERTI: IntegerProperty = IntegerProperty.create("facing_verti", 0, 2) // "Integer" property doesnt accept values below 0, also death to enums, long live magic numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerDefaultState(stateDefinition.any().setValue(FACING_HORIZ, Direction.NORTH).setValue(FACING_VERTI, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = ScreenEntity(blockPos, blockState)
|
||||||
|
|
||||||
|
override fun useWithoutItem(
|
||||||
|
blockState: BlockState,
|
||||||
|
level: Level,
|
||||||
|
blockPos: BlockPos,
|
||||||
|
player: Player,
|
||||||
|
blockHitResult: BlockHitResult
|
||||||
|
): InteractionResult {
|
||||||
|
if(!level.isClientSide) {
|
||||||
|
val sp = player as ServerPlayer
|
||||||
|
val ent = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
|
||||||
|
NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
|
||||||
|
MenuRegistry.openExtendedMenu(sp, object : ExtendedMenuProvider {
|
||||||
|
override fun getDisplayName(): Component = Component.literal("SCREEEEEN!")
|
||||||
|
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
|
||||||
|
// return Menus.SCREEN_MENU.get().create(i, inventory);
|
||||||
|
return ScreenMenu(i, inventory, ent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveExtraData(buf: FriendlyByteBuf?) {
|
||||||
|
buf!!.writeBlockPos(blockPos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return InteractionResult.SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||||
|
builder.add(FACING_HORIZ)
|
||||||
|
builder.add(FACING_VERTI)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||||
|
val horiz = context.horizontalDirection
|
||||||
|
val looking = context.player!!.lookAngle
|
||||||
|
|
||||||
|
val biggest = max(max(abs(looking.y), abs(looking.z)), abs(looking.x))
|
||||||
|
|
||||||
|
return super.getStateForPlacement(context)!!
|
||||||
|
.setValue(FACING_HORIZ, horiz.opposite)
|
||||||
|
.setValue(FACING_VERTI, if (biggest != abs(looking.y)) 1 else if (looking.y < 0) 2 else 0 )
|
||||||
|
|
||||||
|
// val dirs = context.nearestLookingDirections
|
||||||
|
// context.
|
||||||
|
// return when (face) {
|
||||||
|
// Direction.UP -> super.getStateForPlacement(context)!!.setValue(FACING_HORIZ, looking.opposite).setValue(FACING_VERTI, 2)
|
||||||
|
// Direction.DOWN -> super.getStateForPlacement(context)!!.setValue(FACING_HORIZ, looking.opposite).setValue(FACING_VERTI, 0)
|
||||||
|
// else -> super.getStateForPlacement(context)!!.setValue(FACING_HORIZ, looking.opposite).setValue(FACING_VERTI, 1)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.neoflock.neocomputers.datagen
|
||||||
|
|
||||||
|
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())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generateItemModels(itemModelGenerator: ItemModelGenerators) {
|
||||||
|
itemModelGenerator.generateFlatItem(Items.SERVER0.get(), ModelTemplates.FLAT_ITEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.neoflock.neocomputers.datagen
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint
|
||||||
|
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator
|
||||||
|
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
|
||||||
|
class NeoComputersDataGenerator : DataGeneratorEntrypoint {
|
||||||
|
override fun onInitializeDataGenerator(generator: FabricDataGenerator) {
|
||||||
|
val pack: FabricDataGenerator.Pack = generator.createPack()
|
||||||
|
pack.addProvider(::ModelGenerator)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package org.neoflock.neocomputers.entity;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.types.templates.TypeTemplate
|
||||||
|
import com.mojang.serialization.Codec
|
||||||
|
import com.mojang.datafixers.types.Type as DataFixType
|
||||||
|
import dev.architectury.registry.registries.DeferredRegister
|
||||||
|
import dev.architectury.registry.registries.RegistrySupplier
|
||||||
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.util.datafix.DataFixTypes
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.block.Blocks
|
||||||
|
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
|
||||||
|
class BullshitFix: DataFixType<Unit>() {
|
||||||
|
override fun buildTemplate(): TypeTemplate? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildCodec(): Codec<Unit?>? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(
|
||||||
|
o: Any?,
|
||||||
|
ignoreRecursionPoints: Boolean,
|
||||||
|
checkIndex: Boolean
|
||||||
|
): Boolean {
|
||||||
|
return o == this
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object BlockEntities {
|
||||||
|
val BLOCKENTITIES: DeferredRegister<BlockEntityType<*>> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK_ENTITY_TYPE);
|
||||||
|
|
||||||
|
val SCREEN_ENTITY: RegistrySupplier<BlockEntityType<ScreenEntity>> = BLOCKENTITIES.register("screen") {
|
||||||
|
BlockEntityType(
|
||||||
|
::ScreenEntity, setOf(Blocks.SCREEN_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val CAPACITOR_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier1>> = BLOCKENTITIES.register("capacitor") {
|
||||||
|
BlockEntityType(
|
||||||
|
::CapacitorEntityTier1, setOf(Blocks.CAPACITOR_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val CAPACITOR2_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier2>> = BLOCKENTITIES.register("capacitor2") {
|
||||||
|
BlockEntityType(
|
||||||
|
::CapacitorEntityTier2, setOf(Blocks.CAPACITOR_BLOCK2.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val CAPACITOR3_ENTITY: RegistrySupplier<BlockEntityType<CapacitorEntityTier3>> = BLOCKENTITIES.register("capacitor3") {
|
||||||
|
BlockEntityType(
|
||||||
|
::CapacitorEntityTier3, setOf(Blocks.CAPACITOR_BLOCK3.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val SOLARGEN_ENTITY: RegistrySupplier<BlockEntityType<SolarGeneratorBlockEntity>> = BLOCKENTITIES.register("solargen") {
|
||||||
|
BlockEntityType(
|
||||||
|
::SolarGeneratorBlockEntity, setOf(Blocks.SOLARGEN_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val COMBUSTGEN_ENTITY: RegistrySupplier<BlockEntityType<CombustionGeneratorBlockEntity>> = BLOCKENTITIES.register("combustgen") {
|
||||||
|
BlockEntityType(
|
||||||
|
::CombustionGeneratorBlockEntity, setOf(Blocks.COMBUSTGEN_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val REDSTONEIO_ENTITY: RegistrySupplier<BlockEntityType<CombustionGeneratorBlockEntity>> = BLOCKENTITIES.register("redio") {
|
||||||
|
BlockEntityType(
|
||||||
|
::CombustionGeneratorBlockEntity, setOf(Blocks.REDSTONEIO_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val CASE_ENTITY: RegistrySupplier<BlockEntityType<CaseBlockEntity>> = BLOCKENTITIES.register("case") {
|
||||||
|
BlockEntityType(
|
||||||
|
::CaseBlockEntity, setOf(Blocks.CASE_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val CABLE_ENTITY: RegistrySupplier<BlockEntityType<CableEntity>> = BLOCKENTITIES.register("cable") {
|
||||||
|
BlockEntityType(
|
||||||
|
::CableEntity, setOf(Blocks.CABLE_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val RELAY_ENTITY: RegistrySupplier<BlockEntityType<RelayEntity>> = BLOCKENTITIES.register("relay") {
|
||||||
|
BlockEntityType(
|
||||||
|
::RelayEntity, setOf(Blocks.RELAY_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val ROBOT_ENTITY: RegistrySupplier<BlockEntityType<RobotEntity>> = BLOCKENTITIES.register("robot") {
|
||||||
|
BlockEntityType(
|
||||||
|
::RobotEntity, setOf(Blocks.ROBOT_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val RACK_ENTITY: RegistrySupplier<BlockEntityType<RackEntity>> = BLOCKENTITIES.register("rack") {
|
||||||
|
BlockEntityType(
|
||||||
|
::RackEntity, setOf(Blocks.RACK_BLOCK.get()), BullshitFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerPowerBlocks() {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.neoflock.neocomputers.entity
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.world.item.DyeColor
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import org.neoflock.neocomputers.block.CableBlock
|
||||||
|
import org.neoflock.neocomputers.block.CableBlock.Companion.COLOR
|
||||||
|
import org.neoflock.neocomputers.block.CableBlock.Companion.getPropByDirection
|
||||||
|
import org.neoflock.neocomputers.block.DeviceBlockEntity
|
||||||
|
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
|
||||||
|
class CableEntity(pos: BlockPos, state: BlockState) : SingleDeviceBlockEntity(BlockEntities.CABLE_ENTITY.get(), pos, state) {
|
||||||
|
override val deviceNode = object : DeviceNode(){}
|
||||||
|
|
||||||
|
override fun sendCommitsToClient(level: Level) {
|
||||||
|
// we have nothing to commit lol
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun requestServerState() {
|
||||||
|
// no state, we don't bother
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? {
|
||||||
|
if(CableBlock.shouldConnect(blockPos, blockPos.relative(directionToRequester), level!!)) {
|
||||||
|
return deviceNode
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setChanged() {
|
||||||
|
super.setChanged()
|
||||||
|
for (dir in Direction.entries) {
|
||||||
|
val ent = level!!.getBlockEntity(blockPos.relative(dir))
|
||||||
|
level!!.setBlockAndUpdate(blockPos, blockState.setValue(getPropByDirection(dir), CableBlock.shouldConnect(blockPos, blockPos.relative(dir), level!!)))
|
||||||
|
if(ent is DeviceBlockEntity) {
|
||||||
|
ent.connectionsAreDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,307 @@
|
|||||||
|
package org.neoflock.neocomputers.entity
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.resources.sounds.SoundInstance
|
||||||
|
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.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
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
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.SingleDeviceBlockEntity
|
||||||
|
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
||||||
|
import org.neoflock.neocomputers.item.ComponentItem
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
import org.neoflock.neocomputers.network.NNComponent
|
||||||
|
import org.neoflock.neocomputers.network.Networking
|
||||||
|
import org.neoflock.neocomputers.network.PowerRole
|
||||||
|
import org.neoflock.neocomputers.sounds.ComputerRunningSoundInstance
|
||||||
|
import org.neoflock.neocomputers.sounds.Sounds
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainer
|
||||||
|
import java.time.Duration
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||||
|
import kotlin.text.ifEmpty
|
||||||
|
|
||||||
|
class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider {
|
||||||
|
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY)
|
||||||
|
|
||||||
|
var isOn = false
|
||||||
|
var diskActivityTime = 0 // TOOD: writing writers and reading readers
|
||||||
|
var networkActivityTime = 0
|
||||||
|
var err: String? = null
|
||||||
|
var arch = "Lua 5.3"
|
||||||
|
var soundInstance: SoundInstance? = null
|
||||||
|
|
||||||
|
override val deviceNode = object : DeviceNode() {
|
||||||
|
override var powerRole = PowerRole.CONSUMER
|
||||||
|
override var energyCapacity: Long = 500
|
||||||
|
|
||||||
|
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
|
||||||
|
super.writeFullStateCommit(buf)
|
||||||
|
buf.writeUUID(address)
|
||||||
|
buf.writeBoolean(isOn)
|
||||||
|
buf.writeVarInt(diskActivityTime)
|
||||||
|
buf.writeVarInt(networkActivityTime)
|
||||||
|
buf.writeUtf(err ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
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}
|
||||||
|
val redstoneOut = Array(Direction.entries.size) {0}
|
||||||
|
|
||||||
|
fun refetchRedstone(dir: Direction) {
|
||||||
|
val src = blockPos.offset(dir.stepX, dir.stepY, dir.stepZ)
|
||||||
|
val cur = level?.getSignal(src, dir) ?: 0
|
||||||
|
val idx = dir.ordinal
|
||||||
|
if(redstoneIn[idx] != cur) {
|
||||||
|
onRedstoneSignalChanged(dir, redstoneIn[idx], cur)
|
||||||
|
}
|
||||||
|
redstoneIn[idx] = cur
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refetchAllRedstone() {
|
||||||
|
Direction.entries.forEach { refetchRedstone(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendMachineEvent(event: MachineEvent) {
|
||||||
|
stacks.forEach {
|
||||||
|
val item = it.item
|
||||||
|
if(item is ComponentItem) {
|
||||||
|
item.onMachineEvent(it, this, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Networking.emitMessage(deviceNode, Networking.ComputerEvent(deviceNode, event))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
|
||||||
|
sendMachineEvent(MachineRedstoneEvent(this, dir, oldValue, newValue))
|
||||||
|
Networking.emitMessage(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
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMachineBlockPosition(): BlockPos = blockPos
|
||||||
|
override fun getMachineLevel(): Level = level!!
|
||||||
|
|
||||||
|
override fun isRunning(): Boolean = isOn
|
||||||
|
|
||||||
|
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
|
||||||
|
blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn)
|
||||||
|
if(world.isClientSide) {
|
||||||
|
if(value) {
|
||||||
|
soundInstance = ComputerRunningSoundInstance(this, Sounds.COMPUTER_RUNNING.get(), SoundSource.AMBIENT)
|
||||||
|
Minecraft.getInstance().soundManager.play(soundInstance!!)
|
||||||
|
} else {
|
||||||
|
Minecraft.getInstance().soundManager.stop(soundInstance!!)
|
||||||
|
soundInstance = null
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 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 architectures = getMachineArchitectures()
|
||||||
|
// Beep patterns taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/server/machine/Machine.scala
|
||||||
|
if(architectures.isEmpty()) {
|
||||||
|
crash("@neocomputers.errors.ENOCPU")
|
||||||
|
beepAsync("-..")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
|
||||||
|
crash("@neocomputers.errors.E2BIG")
|
||||||
|
beepAsync("-..")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// less than 20% energy is bad
|
||||||
|
if(deviceNode.energy < deviceNode.energyCapacity/5) {
|
||||||
|
crash("@neocomputers.errors.ENOENJ")
|
||||||
|
// we add a beep for the special case where we do have a little bit of energy :P
|
||||||
|
if(deviceNode.energy > 0) beepAsync("..")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if(getMachineMemoryTotal() == 0L) {
|
||||||
|
crash("@neocomputers.errors.ENOMEM")
|
||||||
|
beepAsync("-.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if(arch !in architectures) {
|
||||||
|
// Just pick one! TODO: consult EEPROM first
|
||||||
|
arch = architectures.first()
|
||||||
|
}
|
||||||
|
beepAsync(".")
|
||||||
|
setRunning(true)
|
||||||
|
return isOn
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop(): Boolean {
|
||||||
|
if(!isOn) return false
|
||||||
|
setRunning(false)
|
||||||
|
return isOn
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun crash(error: String): Boolean {
|
||||||
|
beepAsync("--")
|
||||||
|
sendMachineEvent(MachineCrashEvent(this, error))
|
||||||
|
setRunning(false)
|
||||||
|
err = error
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLastError(): String? = err
|
||||||
|
|
||||||
|
override fun getMachineNode() = deviceNode
|
||||||
|
|
||||||
|
override fun getRedstoneInput(direction: Direction): Int = redstoneIn[direction.ordinal]
|
||||||
|
|
||||||
|
override fun getRedstoneOutput(direction: Direction): Int = redstoneOut[direction.ordinal]
|
||||||
|
|
||||||
|
override fun setRedstoneOutput(direction: Direction, newValue: Int): Int {
|
||||||
|
val idx = direction.ordinal
|
||||||
|
val old = redstoneOut[idx]
|
||||||
|
redstoneOut[idx] = newValue
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beepAsync(pattern: String, frequency: Int, duration: Duration, volume: Double): Boolean {
|
||||||
|
NodeSynchronizer.emitBeep(level!!, NodeSynchronizer.BeepDataPayload(getMachineBlockPosition(), pattern, frequency, duration, volume))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun signalDiskActivity(delay: Int) {
|
||||||
|
diskActivityTime = max(delay, diskActivityTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun signalNetworkActivity(delay: Int) {
|
||||||
|
networkActivityTime = max(delay, networkActivityTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMachineMemoryTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getMemoryCapacity(it) }.sum().toLong()
|
||||||
|
override fun getMachineMemoryUsed(): Long = 0
|
||||||
|
override fun getMachineComponentsUsed(): Long = deviceNode.getReachable().count { it.getComponent() != null }.toLong()
|
||||||
|
override fun getMachineComponentsTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getComponentCapacity(it) }.sum().toLong()
|
||||||
|
override fun getMachineArchitecture() = arch
|
||||||
|
override fun getMachineArchitectures() = stacks.mapNotNull { (it.item as? ComponentItem)?.getArchitecturesProvided(it) }.flatten().toSet()
|
||||||
|
override fun setMachineArchitecture(arch: String) {
|
||||||
|
if(this.arch == arch) return
|
||||||
|
this.arch = arch
|
||||||
|
if(isRunning()) {
|
||||||
|
stop()
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItems(): NonNullList<ItemStack> = stacks
|
||||||
|
|
||||||
|
override fun stillValid(player: Player): Boolean = !this.isRemoved
|
||||||
|
|
||||||
|
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(tag, getItems(), registries)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||||
|
super.saveAdditional(compoundTag, provider)
|
||||||
|
compoundTag.putLong("energy", deviceNode.energy)
|
||||||
|
//compoundTag.putBoolean("powerOn", isOn)
|
||||||
|
ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDisplayName(): Component? = Component.literal("Computer")
|
||||||
|
override fun createMenu(i: Int, inventory: Inventory, player: Player) = CaseMenu(i, inventory, this)
|
||||||
|
|
||||||
|
override fun setRemoved() {
|
||||||
|
setRunning(false)
|
||||||
|
super.setRemoved()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean = false
|
||||||
|
override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean = false
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
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
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
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.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.SingleDeviceBlockEntity
|
||||||
|
import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
import org.neoflock.neocomputers.network.PowerRole
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainer
|
||||||
|
import org.neoflock.neocomputers.utils.ContainerUtils
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : SingleDeviceBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider {
|
||||||
|
val energyPerTick: Long = 50
|
||||||
|
|
||||||
|
var burningTimeRemaining: Int = 0
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
override fun canPlaceItem(i: Int, itemStack: ItemStack): Boolean {
|
||||||
|
return ContainerUtils.isBurningFuel(itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItems(): NonNullList<ItemStack> = stacks
|
||||||
|
|
||||||
|
override fun stillValid(player: Player): Boolean {
|
||||||
|
return !this.isRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tickDevice(level: Level) {
|
||||||
|
super.tickDevice(level)
|
||||||
|
// TODO: give us a block state tag for active
|
||||||
|
|
||||||
|
// keep combusting and shi
|
||||||
|
if(burningTimeRemaining > 0) {
|
||||||
|
burningTimeRemaining--
|
||||||
|
deviceNode.giveEnergy(energyPerTick)
|
||||||
|
setChanged()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// no point
|
||||||
|
if(deviceNode.energy >= deviceNode.energyCapacity) return;
|
||||||
|
|
||||||
|
// :fire:
|
||||||
|
val fuel = stacks[0]
|
||||||
|
if(fuel.isEmpty) return
|
||||||
|
|
||||||
|
burningTimeRemaining = ContainerUtils.getBurningTime(fuel) ?: 0
|
||||||
|
setChanged()
|
||||||
|
fuel.count--
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDisplayName(): Component? = Component.translatable("block.neocomputers.combustgen")
|
||||||
|
|
||||||
|
override fun createMenu(i: Int, inventory: Inventory, player: Player) = CombustionGeneratorMenu(i, inventory, this)
|
||||||
|
|
||||||
|
override fun setChanged() {
|
||||||
|
super.setChanged()
|
||||||
|
level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.COMBUSTGEN_ACTIVE, burningTimeRemaining > 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||||
|
super.loadAdditional(compoundTag, provider)
|
||||||
|
deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))
|
||||||
|
burningTimeRemaining = compoundTag.getInt("burningTimeRemaining")
|
||||||
|
ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||||
|
super.saveAdditional(compoundTag, provider)
|
||||||
|
compoundTag.putLong("energy", deviceNode.energy)
|
||||||
|
compoundTag.putInt("burningTimeRemaining", burningTimeRemaining)
|
||||||
|
ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package org.neoflock.neocomputers.entity
|
||||||
|
|
||||||
|
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.network.DeviceNode
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
abstract class MachineEvent {
|
||||||
|
abstract val machine: MachineEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MachineRedstoneEvent(override val machine: MachineEntity, val side: Direction, val oldValue: Int, val newValue: Int): MachineEvent()
|
||||||
|
data class MachinePowerEvent(override val machine: MachineEntity, val nowRunning: Boolean): MachineEvent()
|
||||||
|
data class MachineCrashEvent(override val machine: MachineEntity, val error: String): MachineEvent()
|
||||||
|
|
||||||
|
interface 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,
|
||||||
|
// the duration is doubled.
|
||||||
|
// Architectures should only use short ones.
|
||||||
|
fun beepAsync(pattern: String, frequency: Int = 1000, duration: Duration = Duration.ofMillis(200), volume: Double = 1.0): Boolean
|
||||||
|
// Signals that disk activity is happening for at least that delay
|
||||||
|
fun signalDiskActivity(delay: Int)
|
||||||
|
// signal network activity to a machine
|
||||||
|
fun signalNetworkActivity(delay: Int)
|
||||||
|
|
||||||
|
fun isRunning(): Boolean
|
||||||
|
fun start(): Boolean
|
||||||
|
fun stop(): Boolean
|
||||||
|
fun crash(error: String): Boolean
|
||||||
|
fun getLastError(): String?
|
||||||
|
|
||||||
|
// Some metadata
|
||||||
|
fun getMachineMemoryTotal(): Long
|
||||||
|
fun getMachineMemoryUsed(): Long
|
||||||
|
fun getMachineComponentsUsed(): Long
|
||||||
|
fun getMachineComponentsTotal(): Long
|
||||||
|
fun getMachineArchitecture(): String
|
||||||
|
fun getMachineArchitectures(): Set<String>
|
||||||
|
fun setMachineArchitecture(arch: String)
|
||||||
|
|
||||||
|
// Redstone signals
|
||||||
|
fun getRedstoneInput(direction: Direction): Int
|
||||||
|
fun getRedstoneOutput(direction: Direction): Int
|
||||||
|
// returns the old one
|
||||||
|
fun setRedstoneOutput(direction: Direction, newValue: Int): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add dummy machine class which implements the machine entity interface meant to be used on the client
|
||||||
134
src/main/kotlin/org/neoflock/neocomputers/entity/RackEntity.kt
Normal file
134
src/main/kotlin/org/neoflock/neocomputers/entity/RackEntity.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.neoflock.neocomputers.entity
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.ModelPart
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
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,) {
|
||||||
|
val body: ModelPart? = null
|
||||||
|
val name = "Diddyx" //TODO: names
|
||||||
|
|
||||||
|
init {
|
||||||
|
NeoComputers.LOGGER.info("yooo")
|
||||||
|
}
|
||||||
|
}
|
||||||
117
src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt
Normal file
117
src/main/kotlin/org/neoflock/neocomputers/entity/ScreenEntity.kt
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
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.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) :
|
||||||
|
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) {
|
||||||
|
val mEnv = message.machineEvent
|
||||||
|
NeoComputers.LOGGER.info("Got message $mEnv!")
|
||||||
|
if(mEnv is MachinePowerEvent) {
|
||||||
|
if(mEnv.nowRunning) {
|
||||||
|
lastError = null
|
||||||
|
textBuf.fill(0, 0, textBuf.width, textBuf.height, GPUChar(' '))
|
||||||
|
textBuf.set(0, 0, address.toString())
|
||||||
|
}
|
||||||
|
isOn = mEnv.nowRunning
|
||||||
|
markChanged()
|
||||||
|
}
|
||||||
|
if(mEnv is MachineCrashEvent) {
|
||||||
|
lastError = mEnv.error
|
||||||
|
markChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
|
||||||
|
super.encodeScreenData(player, buf)
|
||||||
|
textBuf.encodeContents(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
|
||||||
|
super.writeFullStateCommit(buf)
|
||||||
|
buf.writeUUID(address)
|
||||||
|
buf.writeBoolean(isOn)
|
||||||
|
buf.writeUtf(lastError ?: "")
|
||||||
|
textBuf.encodeContents(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processCommit(buf: FriendlyByteBuf) {
|
||||||
|
super.processCommit(buf)
|
||||||
|
if(Networking.changeNodeAddress(this, buf.readUUID())) createScreenTexture()
|
||||||
|
isOn = buf.readBoolean()
|
||||||
|
lastError = buf.readUtf().ifEmpty { null }
|
||||||
|
textBuf.decodeContents(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getComponent() = NNComponent("screen")
|
||||||
|
}
|
||||||
|
var bound = "screen/unbound"
|
||||||
|
|
||||||
|
val textBuf = TextBuffer(50, 16)
|
||||||
|
|
||||||
|
private var cleanRenderer: () -> Unit = { } // TODO: THIS SUCKS, FIND A BETTER WAY
|
||||||
|
|
||||||
|
override fun tickDevice(level: Level) {
|
||||||
|
super.tickDevice(level)
|
||||||
|
cleanRenderer()
|
||||||
|
createScreenTexture()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setRemoved() {
|
||||||
|
super.setRemoved()
|
||||||
|
bound = "screen/unbound" // ensure no missing texture is displayed
|
||||||
|
cleanRenderer()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createScreenTexture() {
|
||||||
|
bound = "screen/"+deviceNode.address.toString().replace("-", "_")
|
||||||
|
if (level!!.isClientSide) {
|
||||||
|
if(lastError == null) {
|
||||||
|
if(!isOn) {
|
||||||
|
textBuf.fill(0, 0, textBuf.width, textBuf.height)
|
||||||
|
}
|
||||||
|
val renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf)
|
||||||
|
renderer.drawBuffer()
|
||||||
|
cleanRenderer = { renderer.clean() }
|
||||||
|
} else {
|
||||||
|
var trueError = lastError!!
|
||||||
|
if(trueError.startsWith("@")) {
|
||||||
|
val trans = trueError.substring(1)
|
||||||
|
val lang = Language.getInstance()
|
||||||
|
trueError = lang.getOrDefault("neocomputers.computer.errorNoMsg", "Error: ") + lang.getOrDefault(trans)
|
||||||
|
}
|
||||||
|
val throwAwayBuf = TextBuffer(50, 16)
|
||||||
|
val fg = 0xFFFFFF
|
||||||
|
val bg = 0x2B68A6
|
||||||
|
throwAwayBuf.fill(0, 0, throwAwayBuf.width, throwAwayBuf.height, GPUChar(' ', fg, bg))
|
||||||
|
throwAwayBuf.set((throwAwayBuf.width - trueError.length) / 2, throwAwayBuf.height/2, trueError, fg, bg)
|
||||||
|
val renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), throwAwayBuf)
|
||||||
|
renderer.drawBuffer()
|
||||||
|
cleanRenderer = { renderer.clean() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.neoflock.neocomputers.entity
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.core.HolderLookup
|
||||||
|
import net.minecraft.nbt.CompoundTag
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
import org.neoflock.neocomputers.network.PowerRole
|
||||||
|
|
||||||
|
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 tickDevice(level: Level) {
|
||||||
|
super.tickDevice(level)
|
||||||
|
val l = level
|
||||||
|
if(l.isDay) {
|
||||||
|
deviceNode.giveEnergy(energyPerTick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||||
|
super.loadAdditional(compoundTag, provider)
|
||||||
|
deviceNode.energy = compoundTag.getLong("energy")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||||
|
super.saveAdditional(compoundTag, provider)
|
||||||
|
compoundTag.putLong("energy", deviceNode.energy)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package org.neoflock.neocomputers.entity.render
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat
|
||||||
|
import com.mojang.math.Axis
|
||||||
|
import net.minecraft.client.renderer.LightTexture
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource
|
||||||
|
import net.minecraft.client.renderer.RenderStateShard
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.block.CaseBlock
|
||||||
|
import org.neoflock.neocomputers.entity.CaseBlockEntity
|
||||||
|
|
||||||
|
class CaseEntityRenderer(private val context: BlockEntityRendererProvider.Context?) :
|
||||||
|
BlockEntityRenderer<CaseBlockEntity> {
|
||||||
|
|
||||||
|
val OFF = 0xFF5F855E.toInt()
|
||||||
|
val GREEN = 0xFF4EDC5E.toInt()
|
||||||
|
val RED = 0xFFff102B.toInt()
|
||||||
|
|
||||||
|
val BLINKTIME: Long = 10 // in ticks
|
||||||
|
|
||||||
|
val RENDER_TYPE = RenderType.create("nc_case", DefaultVertexFormat.POSITION_COLOR_LIGHTMAP, VertexFormat.Mode.QUADS,
|
||||||
|
RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder()
|
||||||
|
.setShaderState(RenderStateShard.POSITION_COLOR_LIGHTMAP_SHADER)
|
||||||
|
.setLightmapState(RenderStateShard.LightmapStateShard.LIGHTMAP)
|
||||||
|
.createCompositeState(false))
|
||||||
|
|
||||||
|
override fun render(ent: CaseBlockEntity, partialTick: Float, mat: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||||
|
val buffer = bufferSource.getBuffer(RENDER_TYPE);
|
||||||
|
|
||||||
|
mat.pushPose()
|
||||||
|
handleDirection(ent.blockState.getValue(CaseBlock.FACING), mat)
|
||||||
|
|
||||||
|
mat.translate(5/16F, 14/16F, 0.0001F)
|
||||||
|
if (ent.isOn) drawLED(buffer, mat.last(), 3F)
|
||||||
|
else if (ent.getLastError() != null) { // if else hell
|
||||||
|
if ((ent.level!!.dayTime/BLINKTIME) % 2 == 1.toLong()) drawLED(buffer, mat.last(), 3F, RED)
|
||||||
|
else drawLED(buffer, mat.last(), 3F, OFF, packedLight)
|
||||||
|
} else drawLED(buffer, mat.last(), 3F, OFF, packedLight)
|
||||||
|
|
||||||
|
mat.translate(6/16F, 0F, 0F)
|
||||||
|
drawLED(buffer, mat.last(), 2F, if (ent.diskActivityTime > 0) GREEN else OFF, if (ent.diskActivityTime > 0) LightTexture.FULL_BRIGHT else packedLight)
|
||||||
|
|
||||||
|
mat.popPose()
|
||||||
|
|
||||||
|
}
|
||||||
|
private fun drawLED(buffer: VertexConsumer, mat: PoseStack.Pose, width: Float, color: Int = GREEN, light: Int = LightTexture.FULL_BRIGHT) {
|
||||||
|
buffer.addVertex(mat, width/16F, 0F, 0F).setColor(color).setLight(light)
|
||||||
|
buffer.addVertex(mat, width/16F, 1/16F, 0F).setColor(color).setLight(light)
|
||||||
|
buffer.addVertex(mat, 0F, 1/16F, 0F).setColor(color).setLight(light)
|
||||||
|
buffer.addVertex(mat, 0F, 0F, 0F).setColor(color).setLight(light)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleDirection(facing: Direction, mat: PoseStack) {
|
||||||
|
when (facing) {
|
||||||
|
Direction.SOUTH -> { mat.translate(0F, 0F, 1F) }
|
||||||
|
Direction.EAST -> { mat.mulPose(Axis.YP.rotationDegrees(90F)); mat.translate(-1F, 0F, 1F) }
|
||||||
|
Direction.WEST -> { mat.mulPose(Axis.YN.rotationDegrees(90F)); }
|
||||||
|
Direction.NORTH -> {mat.mulPose(Axis.YP.rotationDegrees(180F)); mat.translate(-1F, 0F, 0F) }
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
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.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.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)
|
||||||
|
.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
|
||||||
|
|
||||||
|
poseStack.popPose()
|
||||||
|
renderTag(ent, Component.literal(ent.name), poseStack, bufferSource, packedLight, partialTick)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.disableDepthTest()
|
||||||
|
context.font.drawInBatch(name, halfwidth.toFloat(), 2f, 0xFFFFFF, false, stack.last().pose(), source, Font.DisplayMode.SEE_THROUGH, alpha, light)
|
||||||
|
|
||||||
|
RenderSystem.enableDepthTest()
|
||||||
|
stack.popPose()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package org.neoflock.neocomputers.entity.render
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat
|
||||||
|
import com.mojang.math.Axis
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource
|
||||||
|
import net.minecraft.client.renderer.RenderStateShard
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.block.ScreenBlock
|
||||||
|
import org.neoflock.neocomputers.entity.ScreenEntity
|
||||||
|
|
||||||
|
class ScreenEntityRenderer(val context: BlockEntityRendererProvider.Context?) : BlockEntityRenderer<ScreenEntity> { // TODO: FORGE
|
||||||
|
val RENDER_TYPE: (ResourceLocation) -> RenderType = { t: ResourceLocation ->
|
||||||
|
RenderType.create(
|
||||||
|
"nc_screen",
|
||||||
|
DefaultVertexFormat.POSITION_TEX,
|
||||||
|
VertexFormat.Mode.QUADS,
|
||||||
|
RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder().
|
||||||
|
setShaderState(RenderStateShard.POSITION_TEX_SHADER).
|
||||||
|
setTextureState(RenderStateShard.TextureStateShard(t, false, false))
|
||||||
|
.createCompositeState(false))
|
||||||
|
}
|
||||||
|
override fun render(entity: ScreenEntity, partialTick: Float, mat: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||||
|
if(!entity.isOn && entity.lastError == null) return
|
||||||
|
val facing = entity.blockState.getValue(ScreenBlock.FACING_HORIZ)
|
||||||
|
val vert = entity.blockState.getValue(ScreenBlock.FACING_VERTI)-1
|
||||||
|
|
||||||
|
mat.pushPose()
|
||||||
|
handleDirection(facing, vert, mat)
|
||||||
|
mat.translate(2 / 16f, 2 / 16f, 0.0001f) // am i epstein or am i just retarded
|
||||||
|
|
||||||
|
val width = 3/4F
|
||||||
|
val height = 3/4F
|
||||||
|
val bx = 0F
|
||||||
|
val by = 0F
|
||||||
|
|
||||||
|
val rendertype = RENDER_TYPE(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, entity.bound))
|
||||||
|
val buffer = bufferSource.getBuffer(rendertype)
|
||||||
|
buffer.addVertex(mat.last(), bx+width, by, 0f).setUv(1f, 1f)
|
||||||
|
buffer.addVertex(mat.last(), bx+width, by+height, 0f).setUv(1f, 0f)
|
||||||
|
buffer.addVertex(mat.last(), bx, by+height, 0f).setUv(0f, 0f)
|
||||||
|
buffer.addVertex(mat.last(), bx, by, 0f).setUv(0f, 1f)
|
||||||
|
|
||||||
|
mat.popPose()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleDirection(facing: Direction, vert: Int, mat: PoseStack) { // TODO: separate up and down from cardinal directions
|
||||||
|
// mat.mulPose(Axis.XN.rotationDegrees(vert.toFloat()*90F))
|
||||||
|
// if (vert==0) {
|
||||||
|
// mat.mulPose(Axis.YP.rotationDegrees(90F))
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
when (facing) {
|
||||||
|
Direction.SOUTH -> { mat.translate(0F, 0F, 1F) }
|
||||||
|
Direction.EAST -> { mat.mulPose(Axis.YP.rotationDegrees(90F)); mat.translate(-1F, 0F, 1F) }
|
||||||
|
Direction.WEST -> { mat.mulPose(Axis.YN.rotationDegrees(90F)); }
|
||||||
|
Direction.NORTH -> {mat.mulPose(Axis.YP.rotationDegrees(180F)); mat.translate(-1F, 0F, 0F) }
|
||||||
|
else -> {}
|
||||||
|
// Direction.UP -> { mat.mulPose(Axis.XN.rotationDegrees(90F)); mat.mulPose(Axis.ZP.rotationDegrees(180F)); mat.translate(-1F, 0F, 1F) } // idek
|
||||||
|
// Direction.DOWN -> { mat.mulPose(Axis.XP.rotationDegrees(90F)); mat.mulPose(Axis.ZN.rotationDegrees(180F)); mat.translate(-1F, -1F, 0F) }
|
||||||
|
}
|
||||||
|
mat.mulPose(Axis.XN.rotationDegrees(vert*90F))
|
||||||
|
mat.translate(0F, if (vert==-1) -1F else 0F, if (vert==1) 1F else 0F)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.menu;
|
||||||
|
|
||||||
|
import net.minecraft.world.Container
|
||||||
|
import net.minecraft.world.SimpleContainer
|
||||||
|
import net.minecraft.world.entity.player.Inventory
|
||||||
|
import org.neoflock.neocomputers.entity.CaseBlockEntity
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentSlot
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentSlotRequirement
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainerMenu
|
||||||
|
|
||||||
|
open class CaseMenu : GenericContainerMenu {
|
||||||
|
open val eepromRequirement = ComponentSlotRequirement(1, ComponentRoles.FIRMWARE)
|
||||||
|
open val slotRequirements = listOf(
|
||||||
|
listOf(ComponentSlotRequirement(1, ComponentRoles.CARD), ComponentSlotRequirement(1, ComponentRoles.CARD)),
|
||||||
|
listOf(ComponentSlotRequirement(1, ComponentRoles.COMPUTE), ComponentSlotRequirement(1, ComponentRoles.MEMORY), ComponentSlotRequirement(1, ComponentRoles.MEMORY)),
|
||||||
|
listOf(ComponentSlotRequirement(1, ComponentRoles.STORAGE)),
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(i: Int, inv: Inventory) : this(i, inv, SimpleContainer(7))
|
||||||
|
|
||||||
|
constructor(i: Int, inv: Inventory, container: Container) : super(Menus.CASE_MENU.get(), i, container) {
|
||||||
|
val machine = container as? CaseBlockEntity
|
||||||
|
|
||||||
|
this.addSlot(ComponentSlot(container, 0, 20, 34, machine, eepromRequirement))
|
||||||
|
|
||||||
|
var i = 1
|
||||||
|
for ((col, slotCol) in slotRequirements.withIndex()) {
|
||||||
|
for ((row, slotReq) in slotCol.withIndex()) {
|
||||||
|
this.addSlot(ComponentSlot(container, i, 98+(col*22), 18*(row+1)-2, machine, slotReq))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addInventorySlots(inv, 8, 84)
|
||||||
|
|
||||||
|
// for (int col=1; col<4; col++) {
|
||||||
|
// for (int row=1; row<4; row++) {
|
||||||
|
// int i = (row-1)*3+(col-1);
|
||||||
|
// if(slotmap[tier][i] != null) {
|
||||||
|
// this.addSlot(new ComponentSlot(entity.getContainer(), ((col-1)*3)+row, 98+((col-1)*22), 18*row-2, slotmap[tier][i], tiermap[tier][i]));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.menu
|
||||||
|
|
||||||
|
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.inventory.AbstractContainerMenu
|
||||||
|
import net.minecraft.world.inventory.Slot
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import org.neoflock.neocomputers.gui.widget.DynamicSlot
|
||||||
|
import org.neoflock.neocomputers.utils.ContainerUtils
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainerMenu
|
||||||
|
|
||||||
|
class CombustionFuelSlot(container: Container, slot: Int, x: Int, y: Int): DynamicSlot(container, slot, x, y) {
|
||||||
|
override fun mayPlace(itemStack: ItemStack): Boolean {
|
||||||
|
return ContainerUtils.isBurningFuel(itemStack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CombustionGeneratorMenu: GenericContainerMenu {
|
||||||
|
// Client-side constructor, idk forge tells me to do this
|
||||||
|
constructor(id: Int, inventory: Inventory): this(id, inventory, SimpleContainer(1))
|
||||||
|
|
||||||
|
// Server-side constructor
|
||||||
|
constructor(id: Int, inventory: Inventory, container: Container): super(Menus.COMBUSTGEN_MENU.get(), id, container) {
|
||||||
|
container.startOpen(inventory.player)
|
||||||
|
|
||||||
|
this.addSlot(CombustionFuelSlot(container, 0, 80, 35))
|
||||||
|
|
||||||
|
this.addInventorySlots(inventory, 8, 84)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/main/kotlin/org/neoflock/neocomputers/gui/menu/Menus.kt
Normal file
36
src/main/kotlin/org/neoflock/neocomputers/gui/menu/Menus.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.menu;
|
||||||
|
|
||||||
|
import dev.architectury.registry.menu.MenuRegistry
|
||||||
|
import dev.architectury.registry.registries.DeferredRegister
|
||||||
|
import dev.architectury.registry.registries.RegistrySupplier
|
||||||
|
import net.minecraft.client.gui.screens.MenuScreens
|
||||||
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.world.flag.FeatureFlagSet
|
||||||
|
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.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") { 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 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
192
src/main/kotlin/org/neoflock/neocomputers/gui/menu/RackMenu.kt
Normal file
192
src/main/kotlin/org/neoflock/neocomputers/gui/menu/RackMenu.kt
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
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
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.menu;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.world.SimpleContainer
|
||||||
|
import net.minecraft.world.entity.player.Inventory
|
||||||
|
import org.neoflock.neocomputers.gui.menu.Menus;
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
|
import net.minecraft.world.inventory.MenuType
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import org.neoflock.neocomputers.entity.ScreenEntity
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainerMenu
|
||||||
|
|
||||||
|
class ScreenMenu : GenericContainerMenu {
|
||||||
|
var entity: ScreenEntity? = null
|
||||||
|
|
||||||
|
constructor(i: Int, inv: Inventory, buf: FriendlyByteBuf) : this(i, inv, inv.player.level().getBlockEntity(buf.readBlockPos()))
|
||||||
|
constructor(i: Int, inv: Inventory, entity: BlockEntity?) : super(Menus.SCREEN_MENU.get(), i, SimpleContainer(0)) {
|
||||||
|
this.entity = entity as ScreenEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.buffer;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.NativeImage
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.renderer.texture.DynamicTexture
|
||||||
|
import net.minecraft.client.renderer.texture.TextureManager
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.utils.FontProvider
|
||||||
|
import org.neoflock.neocomputers.utils.GPUChar
|
||||||
|
import org.neoflock.neocomputers.utils.TextBuffer
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.experimental.and
|
||||||
|
import kotlin.experimental.xor
|
||||||
|
|
||||||
|
class BufferRenderer(private var id: ResourceLocation, private var buffer: TextBuffer) { // TODO: NN buffer
|
||||||
|
val CHARW = 8
|
||||||
|
val CHARH = 16
|
||||||
|
|
||||||
|
private var texwidth: Int = buffer.width*CHARW;
|
||||||
|
private var texheight: Int = buffer.height*CHARH;
|
||||||
|
private var image: NativeImage = NativeImage(texwidth, texheight, true); // idk what the boolean is
|
||||||
|
private var tex: DynamicTexture = DynamicTexture(image)
|
||||||
|
|
||||||
|
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..<buffer.width) {
|
||||||
|
for (j in 0..<buffer.height) {
|
||||||
|
var char: GPUChar = buffer.get(i, j)
|
||||||
|
var x = i*CHARW
|
||||||
|
var y = j*CHARH
|
||||||
|
image.fillRect(x, y, CHARW, CHARH, 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.widget
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder
|
||||||
|
import com.mojang.blaze3d.vertex.BufferUploader
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import com.mojang.blaze3d.vertex.Tesselator
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
|
import net.minecraft.client.gui.components.AbstractWidget
|
||||||
|
import net.minecraft.client.gui.narration.NarrationElementOutput
|
||||||
|
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent
|
||||||
|
import net.minecraft.client.renderer.GameRenderer
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
// #66CC66
|
||||||
|
|
||||||
|
object ProgressBar {
|
||||||
|
|
||||||
|
// NOTE: OC never uses a different width and height
|
||||||
|
fun render(guiGraphics: GuiGraphics, x: Int, y: Int, value: Long, max: Long, mouseX: Int, mouseY: Int, width: Int=142, height: Int=14, tooltipfunc: (Int) -> String?) {
|
||||||
|
RenderSystem.disableBlend()
|
||||||
|
guiGraphics.fill(x, y, x+width-1, y+1, 0xFF373737.toInt()) // top left corner + top edge
|
||||||
|
guiGraphics.fill(x, y+1, x+1, y+height-1, 0xFF373737.toInt()) // left edge
|
||||||
|
|
||||||
|
guiGraphics.fill(x, y+height-1, x+1, y+height, 0xFF8B8B8B.toInt()) // bottom left corner
|
||||||
|
guiGraphics.fill(x+width-1, y, x+width, y+1, 0xFF8B8B8B.toInt()) // top right corner
|
||||||
|
|
||||||
|
guiGraphics.fill(x+1, y+height-1, x+width, y+height, 0xFFFFFFFF.toInt()) // bottom right corner + bottom edge
|
||||||
|
guiGraphics.fill(x+width-1, y+height-1, x+width, y+1, 0xFFFFFFFF.toInt()) // right edge
|
||||||
|
|
||||||
|
val frac = if(max == 0L) 0.0f else value.toFloat() / max.toFloat()
|
||||||
|
val linew = ceil(frac*(width-2).toFloat())
|
||||||
|
guiGraphics.fill(
|
||||||
|
RenderType.gui(),
|
||||||
|
x + 1,
|
||||||
|
y + 1,
|
||||||
|
x + 1 + (linew.toInt()),
|
||||||
|
y + height-1,
|
||||||
|
0xFF66CC66.toInt()
|
||||||
|
)
|
||||||
|
|
||||||
|
val tooltip = tooltipfunc((ceil(frac * 100F)).toInt()) ?: return
|
||||||
|
if (mouseX > x && mouseX < x+width && mouseY > y && mouseY < y+height )
|
||||||
|
guiGraphics.renderTooltip(Minecraft.getInstance().font, Component.literal(tooltip), mouseX, mouseY)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.render;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.GlConst
|
||||||
|
import com.mojang.blaze3d.platform.NativeImage
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
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.VertexFormat
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
|
import net.minecraft.client.renderer.GameRenderer
|
||||||
|
import net.minecraft.client.renderer.texture.DynamicTexture
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
|
||||||
|
class ScreenRenderer {
|
||||||
|
val BORDERS: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/borders.png")
|
||||||
|
var bound: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen/unbound")
|
||||||
|
|
||||||
|
val bordersize = 10
|
||||||
|
|
||||||
|
fun render(graphics: GuiGraphics, x: Int, y: Int, width: Int, height: Int) {
|
||||||
|
RenderSystem.setShader { GameRenderer.getPositionTexShader() }
|
||||||
|
RenderSystem.setShaderTexture(0, BORDERS);
|
||||||
|
// borders
|
||||||
|
val bordersize = 8; // TODO: this should scale i think
|
||||||
|
// top left corner, uv (0, 0) to (6,6)
|
||||||
|
// graphics.blit
|
||||||
|
drawQuad(graphics, x-bordersize, y-bordersize, bordersize, bordersize, 0F, 0F, 6F, 6F);
|
||||||
|
// top border, uv (7,0) to (8,6)
|
||||||
|
drawQuad(graphics, x, y-bordersize, width, bordersize, 7F, 0F, 8F, 6F); // x+bordersize-bordersize
|
||||||
|
// top right corner, uv (9,0) to (15, 6)
|
||||||
|
drawQuad(graphics, x+width, y-bordersize, bordersize, bordersize, 9F, 0F, 15F, 6F);
|
||||||
|
|
||||||
|
// left border, uv (0,7) to (6, 8)
|
||||||
|
drawQuad(graphics, x-bordersize, y, bordersize, height, 0F, 7F, 6F, 8F);
|
||||||
|
// middle, uv (7, 7) to (8, 8)
|
||||||
|
// drawQuad(graphics, x+bordersize, y+bordersize, width-bordersize, height-bordersize, 7, 7, 8, 8);
|
||||||
|
// right border uv (9, 7) to (15, 8)
|
||||||
|
drawQuad(graphics, x+width, y, bordersize, height, 9F, 7F, 15F, 8F);
|
||||||
|
|
||||||
|
// bottom left corner, uv (0, 9) to (6, 15)
|
||||||
|
drawQuad(graphics, x-bordersize, y+height, bordersize, bordersize, 0F, 9F, 6F, 15F);
|
||||||
|
// bottom border, uv (7, 9) to (8, 15)
|
||||||
|
drawQuad(graphics, x, y+height, width, bordersize, 7F, 9F, 8F, 15F);
|
||||||
|
// bottom right corner, uv (9, 9) to (15, 15)
|
||||||
|
drawQuad(graphics, x+width, y+height, bordersize, bordersize, 9F, 9F, 15F, 15F);
|
||||||
|
|
||||||
|
RenderSystem.setShaderTexture(0, bound);
|
||||||
|
RenderSystem.texParameter(GlConst.GL_TEXTURE_2D, GlConst.GL_TEXTURE_MIN_FILTER, GlConst.GL_NEAREST);
|
||||||
|
RenderSystem.texParameter(GlConst.GL_TEXTURE_2D, GlConst.GL_TEXTURE_MAG_FILTER, GlConst.GL_NEAREST);
|
||||||
|
|
||||||
|
drawQuad(graphics, x, y, width, height, 0F, 0F, 15F, 15F);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawQuad(graphics: GuiGraphics, 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()!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind(id: ResourceLocation) {
|
||||||
|
bound = id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unbind() {
|
||||||
|
bound = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen/unbound")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object Static {
|
||||||
|
val img: NativeImage = NativeImage(1, 1, false)
|
||||||
|
val tex: DynamicTexture = DynamicTexture(img)
|
||||||
|
|
||||||
|
fun genUnboundTex() {
|
||||||
|
img.fillRect(0, 0, 1, 1, 0xFF000000.toInt())
|
||||||
|
tex.upload()
|
||||||
|
|
||||||
|
Minecraft.getInstance().textureManager.register(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen/unbound"), tex)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanUnboundTex() {
|
||||||
|
img.close()
|
||||||
|
tex.close()
|
||||||
|
Minecraft.getInstance().textureManager.release(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen/unbound"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.screen;
|
||||||
|
|
||||||
|
import io.netty.buffer.Unpooled
|
||||||
|
import net.minecraft.ChatFormatting
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
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 net.minecraft.world.inventory.tooltip.TooltipComponent
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||||
|
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ButtonSprites
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ImagerButton
|
||||||
|
import org.neoflock.neocomputers.sounds.Sounds
|
||||||
|
import org.neoflock.neocomputers.utils.Formatting
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
|
class CaseScreen : GenericContainerScreen<CaseMenu> {
|
||||||
|
private val PCB: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/computer.png")
|
||||||
|
private val BTN: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/button_power.png")
|
||||||
|
|
||||||
|
private var btn: ImagerButton? = null
|
||||||
|
override fun shouldCenterTitle(): Boolean = false
|
||||||
|
// override fun findMenuTexture(): ResourceLocation = BG
|
||||||
|
|
||||||
|
var isOn = false
|
||||||
|
var lastError: String? = null
|
||||||
|
var energy: Long = 0L
|
||||||
|
var maxEnergy: Long = 0L
|
||||||
|
var memory: Long = 0L
|
||||||
|
var maxMemory: Long = 0L
|
||||||
|
var components: Long = 0L
|
||||||
|
var maxComponents: Long = 0L
|
||||||
|
var arch = ""
|
||||||
|
|
||||||
|
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
|
||||||
|
super.processScreenStatePacket(buf)
|
||||||
|
isOn = buf.readBoolean()
|
||||||
|
btn?.pressed = isOn
|
||||||
|
val error = buf.readByteArray().decodeToString()
|
||||||
|
if(error.isEmpty()) {
|
||||||
|
lastError = null
|
||||||
|
} else {
|
||||||
|
lastError = error
|
||||||
|
}
|
||||||
|
|
||||||
|
energy = buf.readLong()
|
||||||
|
maxEnergy = buf.readLong()
|
||||||
|
memory = buf.readLong()
|
||||||
|
maxMemory = buf.readLong()
|
||||||
|
components = buf.readLong()
|
||||||
|
maxComponents = buf.readLong()
|
||||||
|
arch = buf.readUtf()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getErrorComponent(err: String): Component = if(err.startsWith("@")) Component.translatable(err.substring(1)) else Component.literal(err)
|
||||||
|
|
||||||
|
fun computeButtonTooltip(): List<Component> {
|
||||||
|
val msgs = mutableListOf(Component.translatable(if(isOn) "neocomputers.computer.on" else "neocomputers.computer.off").withStyle(if(isOn) ChatFormatting.GREEN else ChatFormatting.RED))
|
||||||
|
if(lastError != null) {
|
||||||
|
msgs.addLast(Component.translatable("neocomputers.computer.errorNoMsg").withStyle(ChatFormatting.RED).append(getErrorComponent(lastError!!)))
|
||||||
|
}
|
||||||
|
if(arch.isNotEmpty()) {
|
||||||
|
msgs.addLast(Component.translatable("neocomputers.arch", arch))
|
||||||
|
}
|
||||||
|
if(hasShiftDown()) {
|
||||||
|
msgs.addLast(Component.translatable("neocomputers.computer.energy", energy, maxEnergy).withStyle(if(energy < maxEnergy/5) ChatFormatting.RED else ChatFormatting.WHITE))
|
||||||
|
msgs.addLast(Component.translatable("neocomputers.computer.memory", Formatting.formatMemory(memory), Formatting.formatMemory(maxMemory)))
|
||||||
|
msgs.addLast(Component.translatable("neocomputers.computer.components", components, maxComponents).withStyle(if(components <= maxComponents) ChatFormatting.WHITE else ChatFormatting.RED))
|
||||||
|
}
|
||||||
|
return msgs
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(abstractContainerMenu: CaseMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
|
||||||
|
btn = ImagerButton(
|
||||||
|
71, 33,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) {
|
||||||
|
super.renderCustomOverlay(graphics, mouseX, mouseY, blend)
|
||||||
|
if(btn!!.isHovered) {
|
||||||
|
graphics.renderTooltip(this.font, computeButtonTooltip(), Optional.empty<TooltipComponent>(), mouseX, mouseY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.screen
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.world.entity.player.Inventory
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.entity.BlockEntities
|
||||||
|
import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ProgressBar
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
class CombustionGeneratorScreen : GenericContainerScreen<CombustionGeneratorMenu> {
|
||||||
|
// val bar: ProgressBar = ProgressBar(x = -50, y = -50) { Pair(energy, energyCapacity) } // hide it type shi
|
||||||
|
constructor(abstractContainerMenu: CombustionGeneratorMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component)
|
||||||
|
|
||||||
|
var energy: Long = 0
|
||||||
|
var energyCapacity: Long = 1
|
||||||
|
|
||||||
|
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
|
||||||
|
super.render(graphics, mouseX, mouseY, something)
|
||||||
|
|
||||||
|
var relX: Int = (this.width - this.imageWidth) / 2;
|
||||||
|
var relY: Int = (this.height - this.imageHeight) / 2;
|
||||||
|
|
||||||
|
ProgressBar.render(graphics, relX+17, relY+55, energy, energyCapacity, mouseX, mouseY) {
|
||||||
|
String.format("Energy: %d%% (%d/%d)", it, energy, energyCapacity)
|
||||||
|
}
|
||||||
|
// bar.x = imageX + 17
|
||||||
|
// bar.y = imageY + 50
|
||||||
|
|
||||||
|
// val lineBg = 0xFF002200.toInt()
|
||||||
|
// val lineFg = 0xFF00FF00.toInt()
|
||||||
|
|
||||||
|
// val lineX = imageX + 8
|
||||||
|
// val lineY = imageY + 6
|
||||||
|
// val lineHeight = 60
|
||||||
|
|
||||||
|
// val power = energy.toDouble() / energyCapacity
|
||||||
|
|
||||||
|
// graphics.fill(lineX, lineY, lineX + 2, lineY + lineHeight, lineFg)
|
||||||
|
// graphics.fill(lineX, lineY, lineX + 2, lineY + (lineHeight * (1.0 - power)).toInt(), lineBg)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBoundBlockEntityType() = setOf(BlockEntities.COMBUSTGEN_ENTITY.get())
|
||||||
|
|
||||||
|
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
|
||||||
|
energy = buf.readLong()
|
||||||
|
energyCapacity = buf.readLong()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.screen;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||||
|
import com.mojang.blaze3d.vertex.Tesselator
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat
|
||||||
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.world.entity.player.Inventory
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.entity.ScreenEntity
|
||||||
|
import org.neoflock.neocomputers.gui.buffer.BufferRenderer
|
||||||
|
import org.neoflock.neocomputers.gui.menu.ScreenMenu
|
||||||
|
import org.neoflock.neocomputers.gui.render.ScreenRenderer
|
||||||
|
import org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||||
|
import org.neoflock.neocomputers.utils.TextBuffer
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class ScreenScreen : GenericContainerScreen<ScreenMenu>{
|
||||||
|
private var renderer: ScreenRenderer = ScreenRenderer();
|
||||||
|
|
||||||
|
var textBuf = TextBuffer(0, 0)
|
||||||
|
|
||||||
|
override fun shouldCenterTitle(): Boolean = false
|
||||||
|
|
||||||
|
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
|
||||||
|
super.processScreenStatePacket(buf)
|
||||||
|
textBuf.decodeContents(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(abstractContainerMenu: ScreenMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
|
||||||
|
var ent: ScreenEntity = abstractContainerMenu.entity!!;
|
||||||
|
renderer.bind(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, ent.bound))
|
||||||
|
// advanced graphics programming
|
||||||
|
this.titleLabelX = Int.MAX_VALUE
|
||||||
|
this.inventoryLabelX = Int.MAX_VALUE
|
||||||
|
}
|
||||||
|
override fun renderBg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {
|
||||||
|
if(textBuf.width > 0) {
|
||||||
|
imageWidth = textBuf.width * 4
|
||||||
|
imageHeight = textBuf.height * 8
|
||||||
|
renderer.render(guiGraphics, imageX, imageY, imageWidth, imageHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) { }
|
||||||
|
|
||||||
|
// override fun onClose() {
|
||||||
|
// super.onClose()
|
||||||
|
// renderer.
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.widget
|
||||||
|
|
||||||
|
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.entity.player.Player
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
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
|
||||||
|
|
||||||
|
object ComponentRoles {
|
||||||
|
// For card slots
|
||||||
|
val CARD = "card"
|
||||||
|
// For memory slots
|
||||||
|
val MEMORY = "memory"
|
||||||
|
// For storage slots, aside from EEPROMs and floppys
|
||||||
|
val STORAGE = "storage"
|
||||||
|
// For floppy drive slots
|
||||||
|
val FLOPPY = "floppy"
|
||||||
|
// For EEPROM slots
|
||||||
|
val FIRMWARE = "firmware"
|
||||||
|
// For CPU slot
|
||||||
|
val COMPUTE = "compute"
|
||||||
|
// For component bus
|
||||||
|
val BUS = "bus"
|
||||||
|
val TOOL = "tool"
|
||||||
|
val UPGRADE = "upgrade"
|
||||||
|
val CONTAINER = "container"
|
||||||
|
val TABLET = "tablet"
|
||||||
|
val RACK_MOUNTABLE = "rack"
|
||||||
|
// Conventional network cards, like LAN, WLAN0, WLAN1, etc.
|
||||||
|
val NETWORK = "network"
|
||||||
|
// Internet cards
|
||||||
|
val INET = "internet"
|
||||||
|
|
||||||
|
val MISSING_ROLE_TEXTURE = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/slots/na.png")
|
||||||
|
|
||||||
|
val textureMap = mutableMapOf<String, ResourceLocation>()
|
||||||
|
|
||||||
|
fun mapTexture(role: String, texture: ResourceLocation) {
|
||||||
|
textureMap[role] = texture
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTextureFor(role: String) = textureMap[role] ?: MISSING_ROLE_TEXTURE
|
||||||
|
|
||||||
|
fun mapDefaultTextures() {
|
||||||
|
val core = mapOf(
|
||||||
|
CARD to "card",
|
||||||
|
BUS to "component_bus",
|
||||||
|
CONTAINER to "container",
|
||||||
|
COMPUTE to "cpu",
|
||||||
|
FIRMWARE to "eeprom",
|
||||||
|
FLOPPY to "floppy",
|
||||||
|
STORAGE to "hdd",
|
||||||
|
MEMORY to "memory",
|
||||||
|
TABLET to "tablet",
|
||||||
|
TOOL to "tool",
|
||||||
|
UPGRADE to "upgrade",
|
||||||
|
RACK_MOUNTABLE to "rack_mountable",
|
||||||
|
// TODO: give them proper textures
|
||||||
|
NETWORK to "card",
|
||||||
|
INET to "card",
|
||||||
|
)
|
||||||
|
for((role, tex) in core) {
|
||||||
|
mapTexture(role, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/slots/$tex.png"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ComponentSlotRequirement(val tier: Int, val role: String) {
|
||||||
|
fun allowsItemStack(itemStack: ItemStack): Boolean {
|
||||||
|
val item = itemStack.item
|
||||||
|
if(item !is ComponentItem) return false
|
||||||
|
if(tier > 0 && item.getComponentTier(itemStack) > tier) return false
|
||||||
|
return item.getComponentRoles(itemStack).contains(role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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: ComponentUser?, val 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(graphics, ComponentRoles.getTextureFor(requirement.role), x - 1, y - 1, 18, 18, 0F, 0F, 15F, 15F)
|
||||||
|
if (requirement.tier > 0) {
|
||||||
|
RenderSystem.setShaderTexture(
|
||||||
|
0,
|
||||||
|
ResourceLocation.fromNamespaceAndPath(
|
||||||
|
NeoComputers.MODID,
|
||||||
|
"textures/gui/slots/tier${requirement.tier - 1}.png"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mayPlace(itemStack: ItemStack): Boolean {
|
||||||
|
if(!requirement.allowsItemStack(itemStack)) return false
|
||||||
|
return super.mayPlace(itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(itemStack: ItemStack) {
|
||||||
|
super.set(itemStack)
|
||||||
|
if(machine != null) {
|
||||||
|
val item = itemStack.item
|
||||||
|
if (item is ComponentItem) {
|
||||||
|
item.whenComponentPlaced(itemStack, machine, requirement.role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTake(player: Player, itemStack: ItemStack) {
|
||||||
|
if(machine != null) {
|
||||||
|
val item = itemStack.item
|
||||||
|
if (item is ComponentItem) {
|
||||||
|
item.whenComponentTaken(itemStack, machine, requirement.role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onTake(player, itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMaxStackSize(): Int = 1
|
||||||
|
override fun getMaxStackSize(itemStack: ItemStack): Int = 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.widget
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
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) {
|
||||||
|
|
||||||
|
val BACKGROUND: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/slots/slot.png")
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun draw(graphics: GuiGraphics, mouseX: Int, mouseY: Int) {
|
||||||
|
drawQuad(graphics, BACKGROUND, x-1, y-1, 18, 18, 0F, 0F, 15F, 15F)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.neoflock.neocomputers.gui.widget
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.Gui
|
||||||
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
|
import net.minecraft.client.gui.components.Button
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
|
||||||
|
data class ButtonSprites(val sheet: ResourceLocation, val spriteWidth: Int, val spriteHeight: Int, val texWidth: Int, val texHeight: Int)
|
||||||
|
|
||||||
|
// minecraft sux
|
||||||
|
class ImagerButton(x: Int, y: Int, width: Int, height: Int, val sprites: ButtonSprites, onPress: Button.OnPress) : Button(x, y, width, height, Component.literal(""), onPress, DEFAULT_NARRATION) {
|
||||||
|
// public void renderWidget(GuiGraphics guiGraphics, int i, int j, float f) {
|
||||||
|
// ResourceLocation resourceLocation = this.sprites.get(this.isActive(), this.isHoveredOrFocused());
|
||||||
|
// guiGraphics.blitSprite(resourceLocation, this.getX(), this.getY(), this.width, this.height);
|
||||||
|
// }
|
||||||
|
var pressed = false
|
||||||
|
|
||||||
|
override fun renderWidget(graphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
val u = if (pressed) 18F else 0F // no clue why it's swapped? prob cooked the coordinates, we gotta get parchment so bad
|
||||||
|
val v = if (this.isHoveredOrFocused) 18F else 0F
|
||||||
|
|
||||||
|
graphics.blit(sprites.sheet, x, y, width, height, u, v, sprites.spriteWidth, sprites.spriteHeight, sprites.texWidth, sprites.texHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
20
src/main/kotlin/org/neoflock/neocomputers/item/CBUSItem.kt
Normal file
20
src/main/kotlin/org/neoflock/neocomputers/item/CBUSItem.kt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
open class CBUSItem(val tier: Int, val maxComponents: Int): Item(Item.Properties()), ComponentItem {
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack) = setOf(ComponentRoles.BUS)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack) = tier
|
||||||
|
|
||||||
|
override fun getComponentCapacity(itemStack: ItemStack) = maxComponents
|
||||||
|
|
||||||
|
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
|
||||||
|
}
|
||||||
|
class CBUS0: CBUSItem(1, 8)
|
||||||
|
class CBUS1: CBUSItem(2, 12)
|
||||||
|
class CBUS2: CBUSItem(3, 16)
|
||||||
|
class CBUSCreative: CBUSItem(1, 1024)
|
||||||
24
src/main/kotlin/org/neoflock/neocomputers/item/CPUItem.kt
Normal file
24
src/main/kotlin/org/neoflock/neocomputers/item/CPUItem.kt
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
open class CPUItem(val tier: Int, val maxComponents: Int): Item(Item.Properties()), RelayUpgrade {
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.COMPUTE)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack): Int = tier
|
||||||
|
|
||||||
|
override fun getComponentCapacity(itemStack: ItemStack): Int = maxComponents
|
||||||
|
|
||||||
|
override fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf("Lua 5.3")
|
||||||
|
|
||||||
|
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun getRelayInterval(itemStack: ItemStack) = 4 / tier
|
||||||
|
}
|
||||||
|
|
||||||
|
class CPU0: CPUItem(1, 8)
|
||||||
|
class CPU1: CPUItem(2, 12)
|
||||||
|
class CPU2: CPUItem(3, 16)
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import org.neoflock.neocomputers.entity.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.entity.MachineEntity
|
||||||
|
import org.neoflock.neocomputers.entity.MachineEvent
|
||||||
|
import org.neoflock.neocomputers.network.DeviceNode
|
||||||
|
import org.neoflock.neocomputers.network.Networking
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
// need not necessarily be just a component, can be upgrades as well
|
||||||
|
interface ComponentItem {
|
||||||
|
fun getComponentRoles(itemStack: ItemStack): Set<String>
|
||||||
|
fun getComponentTier(itemStack: ItemStack): Int
|
||||||
|
|
||||||
|
// Get machine properties they can influence
|
||||||
|
fun getMemoryCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
fun getComponentCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
fun getArchitecturesProvided(itemStack: ItemStack): Set<String> = setOf()
|
||||||
|
|
||||||
|
// Component placed, node must now exist
|
||||||
|
fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
|
||||||
|
val oldNode = getComponentNode(itemStack)
|
||||||
|
if(oldNode != null) Networking.removeNode(oldNode) // did a mod forget to call whenComponentTaken?
|
||||||
|
val node = toComponentNode(itemStack, machine) ?: return
|
||||||
|
Networking.addNode(node)
|
||||||
|
machine?.getMachineNode()?.connectTo(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component taken, and thus removed
|
||||||
|
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: ComponentUser?): DeviceNode?
|
||||||
|
|
||||||
|
// Gets the node associated to an item, if it exists
|
||||||
|
fun getComponentNode(itemStack: ItemStack): DeviceNode? {
|
||||||
|
val address = itemStack.get(DataComponents.ADDRESS) ?: return null
|
||||||
|
val uuid = UUID.fromString(address) ?: return null
|
||||||
|
return Networking.getNode(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ensureHasAddress(itemStack: ItemStack): UUID {
|
||||||
|
if(!itemStack.has(DataComponents.ADDRESS)) {
|
||||||
|
itemStack.set(DataComponents.ADDRESS, UUID.randomUUID().toString())
|
||||||
|
}
|
||||||
|
return UUID.fromString(itemStack.get(DataComponents.ADDRESS))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
45
src/main/kotlin/org/neoflock/neocomputers/item/DataCard.kt
Normal file
45
src/main/kotlin/org/neoflock/neocomputers/item/DataCard.kt
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
import org.neoflock.neocomputers.utils.Formatting
|
||||||
|
|
||||||
|
// Note: We'll prob want to replace them with NN component configs later on
|
||||||
|
|
||||||
|
open class DataCard(val tier: Int, val limit: Long): Item(Properties()), ComponentItem {
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack): Int = tier
|
||||||
|
|
||||||
|
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: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
list.addLast(addrComp)
|
||||||
|
list.addLast(Component.translatable("neocomputers.data.limit", Formatting.formatMemory(limit)))
|
||||||
|
// TODO: show encryption support
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataCard0: DataCard(1, 1 shl 20)
|
||||||
|
class DataCard1: DataCard(2, 1 shl 20)
|
||||||
|
class DataCard2: DataCard(3, 1 shl 20)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec
|
||||||
|
import net.minecraft.core.Registry
|
||||||
|
import net.minecraft.core.component.DataComponentType
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
object DataComponents {
|
||||||
|
val ADDRESS = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "address"),
|
||||||
|
DataComponentType.builder<String>().persistent(Codec.STRING).build())
|
||||||
|
val LABEL = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "label"),
|
||||||
|
DataComponentType.builder<String>().persistent(Codec.STRING).build())
|
||||||
|
val READONLY = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "readonly"),
|
||||||
|
DataComponentType.builder<Boolean>().persistent(Codec.BOOL).build())
|
||||||
|
val ARCH = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "architecture"),
|
||||||
|
DataComponentType.builder<String>().persistent(Codec.STRING).build())
|
||||||
|
|
||||||
|
val EEPROM_CODE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_code"),
|
||||||
|
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
|
||||||
|
val EEPROM_DATA = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_data"),
|
||||||
|
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
|
||||||
|
val EEPROM_CODESIZE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_codesize"),
|
||||||
|
DataComponentType.builder<Int>().persistent(Codec.INT).build())
|
||||||
|
val EEPROM_DATASIZE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_datasize"),
|
||||||
|
DataComponentType.builder<Int>().persistent(Codec.INT).build())
|
||||||
|
|
||||||
|
val TUNNEL_CHANNEL = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "tunnel_channel"),
|
||||||
|
DataComponentType.builder<String>().persistent(Codec.STRING).build())
|
||||||
|
}
|
||||||
71
src/main/kotlin/org/neoflock/neocomputers/item/EEPROMItem.kt
Normal file
71
src/main/kotlin/org/neoflock/neocomputers/item/EEPROMItem.kt
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
import org.neoflock.neocomputers.utils.Formatting
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
fun getEEPROMProperties(codeCap: Int, dataCap: Int): Item.Properties = Item.Properties()
|
||||||
|
.component(DataComponents.EEPROM_CODE, ByteBuffer.allocate(codeCap))
|
||||||
|
.component(DataComponents.EEPROM_DATA, ByteBuffer.allocate(dataCap))
|
||||||
|
.component(DataComponents.EEPROM_CODESIZE, 0)
|
||||||
|
.component(DataComponents.EEPROM_DATASIZE, 0)
|
||||||
|
.component(DataComponents.LABEL, "")
|
||||||
|
.component(DataComponents.READONLY, false)
|
||||||
|
|
||||||
|
open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: Int): Item(getEEPROMProperties(codeCapacity, dataCapacity)), ComponentItem {
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.FIRMWARE)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack): Int = tier
|
||||||
|
|
||||||
|
override fun getMemoryCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
|
||||||
|
override fun getComponentCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
|
||||||
|
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: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val codeSize = itemStack.get(DataComponents.EEPROM_CODESIZE) ?: 0
|
||||||
|
val dataSize = itemStack.get(DataComponents.EEPROM_DATASIZE) ?: 0
|
||||||
|
val arch = itemStack.get(DataComponents.ARCH)
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val readonly = itemStack.get(DataComponents.READONLY) ?: false
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
list.addLast(addrComp)
|
||||||
|
if(arch != null) {
|
||||||
|
list.addLast(Component.translatable("neocomputers.arch", arch))
|
||||||
|
}
|
||||||
|
list.addLast(Component.translatable("neocomputers.eeprom.codeused", Formatting.formatMemory(codeSize.toLong()),
|
||||||
|
Formatting.formatMemory(codeCapacity.toLong())))
|
||||||
|
list.addLast(Component.translatable("neocomputers.eeprom.dataused", Formatting.formatMemory(dataSize.toLong()),
|
||||||
|
Formatting.formatMemory(dataCapacity.toLong())))
|
||||||
|
list.addLast(Component.translatable(if(readonly) "neocomputers.readonly" else "neocomputers.readwrite"))
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(itemStack: ItemStack): Component? {
|
||||||
|
if(itemStack.has(DataComponents.LABEL)) {
|
||||||
|
val label = itemStack.get(DataComponents.LABEL) ?: ""
|
||||||
|
if(label.isNotEmpty()) return Component.literal(label)
|
||||||
|
}
|
||||||
|
return super.getName(itemStack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EEPROM0: EEPROMItem(1, 4096, 256)
|
||||||
44
src/main/kotlin/org/neoflock/neocomputers/item/GPUCard.kt
Normal file
44
src/main/kotlin/org/neoflock/neocomputers/item/GPUCard.kt
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
|
||||||
|
// Note: We'll prob want to replace them with NN component configs later on
|
||||||
|
|
||||||
|
open class GPUCard(val tier: Int, val vram: Long): Item(Properties()), ComponentItem {
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack): Int = tier
|
||||||
|
|
||||||
|
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: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
list.addLast(addrComp)
|
||||||
|
list.addLast(Component.translatable("neocomputers.gpu.vram", vram))
|
||||||
|
// TODO: show VRAM usage and whatnot
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GPUCard0: GPUCard(1, 5000)
|
||||||
|
class GPUCard1: GPUCard(2, 10000)
|
||||||
|
class GPUCard2: GPUCard(3, 20000)
|
||||||
63
src/main/kotlin/org/neoflock/neocomputers/item/HDDItem.kt
Normal file
63
src/main/kotlin/org/neoflock/neocomputers/item/HDDItem.kt
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
import org.neoflock.neocomputers.utils.Formatting
|
||||||
|
|
||||||
|
fun getDiskProperties(): Item.Properties = Item.Properties()
|
||||||
|
.component(DataComponents.LABEL, "")
|
||||||
|
.component(DataComponents.READONLY, false)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
override fun getMemoryCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
|
||||||
|
override fun getComponentCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
|
||||||
|
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: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val readonly = itemStack.get(DataComponents.READONLY) ?: false
|
||||||
|
val spaceUsed: Long = 0
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
list.addLast(addrComp)
|
||||||
|
list.addLast(Component.translatable("neocomputers.disk.spaceused", Formatting.formatMemory(spaceUsed),
|
||||||
|
Formatting.formatMemory(capacity)))
|
||||||
|
list.addLast(Component.translatable(if(readonly) "neocomputers.readonly" else "neocomputers.readwrite"))
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName(itemStack: ItemStack): Component? {
|
||||||
|
if(itemStack.has(DataComponents.LABEL)) {
|
||||||
|
val label = itemStack.get(DataComponents.LABEL) ?: ""
|
||||||
|
if(label.isNotEmpty()) return Component.literal(label)
|
||||||
|
}
|
||||||
|
return super.getName(itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRelayQueueSize(itemStack: ItemStack) = relayQueueSize
|
||||||
|
}
|
||||||
|
|
||||||
|
class HardDisk0: HardDiskItem(1, 1 shl 20, 30)
|
||||||
|
class HardDisk1: HardDiskItem(2, 2 shl 20, 40)
|
||||||
|
class HardDisk2: HardDiskItem(3, 4 shl 20, 50)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
|
||||||
|
class InternetCard: Item(Item.Properties()), ComponentItem {
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD, ComponentRoles.INET)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack): Int = 2
|
||||||
|
|
||||||
|
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: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
list.addLast(addrComp)
|
||||||
|
// TODO: show HTTP/TCP/TLS support
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/main/kotlin/org/neoflock/neocomputers/item/Items.kt
Normal file
51
src/main/kotlin/org/neoflock/neocomputers/item/Items.kt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
import dev.architectury.registry.registries.DeferredRegister
|
||||||
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.world.item.Item
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
|
||||||
|
object Items {
|
||||||
|
val ITEMS: DeferredRegister<Item> = DeferredRegister.create(NeoComputers.MODID, Registries.ITEM)
|
||||||
|
|
||||||
|
val MEM0 = ITEMS.register("memory0") { MemoryTier1() }
|
||||||
|
val MEM1 = ITEMS.register("memory1") { MemoryTier1_5() }
|
||||||
|
val MEM2 = ITEMS.register("memory2") { MemoryTier2() }
|
||||||
|
val MEM3 = ITEMS.register("memory3") { MemoryTier2_5() }
|
||||||
|
val MEM4 = ITEMS.register("memory4") { MemoryTier3() }
|
||||||
|
val MEM5 = ITEMS.register("memory5") { MemoryTier3_5() }
|
||||||
|
|
||||||
|
val EE0 = ITEMS.register("eeprom0") { EEPROM0() }
|
||||||
|
|
||||||
|
val CPU0 = ITEMS.register("cpu0") { CPU0() }
|
||||||
|
val CPU1 = ITEMS.register("cpu1") { CPU1() }
|
||||||
|
val CPU2 = ITEMS.register("cpu2") { CPU2() }
|
||||||
|
|
||||||
|
val CBUS0 = ITEMS.register("cbus0") { CBUS0() }
|
||||||
|
val CBUS1 = ITEMS.register("cbus1") { CBUS1() }
|
||||||
|
val CBUS2 = ITEMS.register("cbus2") { CBUS2() }
|
||||||
|
val CBUS_CREATIVE = ITEMS.register("cbus_creative") { CBUSCreative() }
|
||||||
|
|
||||||
|
val INET = ITEMS.register("inet") { InternetCard() }
|
||||||
|
val TUNNEL = ITEMS.register("tunnel") { TunnelCard() }
|
||||||
|
val LAN = ITEMS.register("lan") { LANCard() }
|
||||||
|
val WLAN0 = ITEMS.register("wlan0") { WLANCard0() }
|
||||||
|
val WLAN1 = ITEMS.register("wlan1") { WLANCard1() }
|
||||||
|
|
||||||
|
val DATA0 = ITEMS.register("data0") { DataCard0() }
|
||||||
|
val DATA1 = ITEMS.register("data1") { DataCard1() }
|
||||||
|
val DATA2 = ITEMS.register("data2") { DataCard2() }
|
||||||
|
|
||||||
|
val GPU0 = ITEMS.register("gpu0") { GPUCard0() }
|
||||||
|
val GPU1 = ITEMS.register("gpu1") { GPUCard1() }
|
||||||
|
val GPU2 = ITEMS.register("gpu2") { GPUCard2() }
|
||||||
|
|
||||||
|
val HDD0 = ITEMS.register("hdd0") { HardDisk0() }
|
||||||
|
val HDD1 = ITEMS.register("hdd1") { HardDisk1() }
|
||||||
|
val HDD2 = ITEMS.register("hdd2") { HardDisk2() }
|
||||||
|
|
||||||
|
val REDIO0 = ITEMS.register("redio0") { RedstoneCard0() }
|
||||||
|
val REDIO1 = ITEMS.register("redio1") { RedstoneCard1() }
|
||||||
|
|
||||||
|
val SERVER0 = ITEMS.register("server0") { ServerItem() }
|
||||||
|
}
|
||||||
43
src/main/kotlin/org/neoflock/neocomputers/item/MemoryItem.kt
Normal file
43
src/main/kotlin/org/neoflock/neocomputers/item/MemoryItem.kt
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
import org.neoflock.neocomputers.utils.Formatting
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
override fun getMemoryCapacity(itemStack: ItemStack): Int = capacity
|
||||||
|
|
||||||
|
override fun getComponentCapacity(itemStack: ItemStack): Int = 0
|
||||||
|
|
||||||
|
// no node for memory
|
||||||
|
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
list.addLast(Component.translatable("neocomputers.memory.capacity", Formatting.formatMemory(capacity.toLong())))
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRelayBufferSize(itemStack: ItemStack) = relayBuf
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
|
||||||
|
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: ComponentUser?, newRole: String) {
|
||||||
|
if(machine != null) ensureHasAddress(itemStack)
|
||||||
|
super.whenComponentPlaced(itemStack, machine, newRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Modem Component
|
||||||
|
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
list.addLast(addrComp)
|
||||||
|
if(maxRange > 0) {
|
||||||
|
list.addLast(Component.translatable("neocomputers.wlan.range", maxRange))
|
||||||
|
}
|
||||||
|
// TODO: show max packet size and whatnot
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LANCard: NetworkCard(1, 0, true)
|
||||||
|
class WLANCard0: NetworkCard(1, 16, true)
|
||||||
|
class WLANCard1: NetworkCard(1, 400, true)
|
||||||
62
src/main/kotlin/org/neoflock/neocomputers/item/RackItem.kt
Normal file
62
src/main/kotlin/org/neoflock/neocomputers/item/RackItem.kt
Normal 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
|
||||||
|
// Note: We'll prob want to replace them with NN component configs later on
|
||||||
|
|
||||||
|
open class RedstoneCard(val tier: Int): Item(Properties()), ComponentItem {
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack): Int = tier
|
||||||
|
|
||||||
|
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: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
// TODO: show redstone and whatnot
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RedstoneCard0: RedstoneCard(1)
|
||||||
|
class RedstoneCard1: RedstoneCard(2)
|
||||||
26
src/main/kotlin/org/neoflock/neocomputers/item/ServerItem.kt
Normal file
26
src/main/kotlin/org/neoflock/neocomputers/item/ServerItem.kt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource
|
||||||
|
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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/main/kotlin/org/neoflock/neocomputers/item/Tabs.kt
Normal file
83
src/main/kotlin/org/neoflock/neocomputers/item/Tabs.kt
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
import dev.architectury.registry.CreativeTabRegistry
|
||||||
|
import dev.architectury.registry.registries.DeferredRegister
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.world.item.CreativeModeTab
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
object Tabs {
|
||||||
|
val TABS: DeferredRegister<CreativeModeTab> =
|
||||||
|
DeferredRegister.create(NeoComputers.MODID, Registries.CREATIVE_MODE_TAB)
|
||||||
|
|
||||||
|
val TAB = TABS.register("neocomputers_tab") {
|
||||||
|
// its only experimental once they change it
|
||||||
|
CreativeTabRegistry.create { builder ->
|
||||||
|
builder.title(Component.literal("NeoComputers"))
|
||||||
|
builder.icon {
|
||||||
|
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()))
|
||||||
|
output.accept(ItemStack(Items.SERVER0.get()))
|
||||||
|
|
||||||
|
// Criminal black magic to put LuaBIOS EEPROM in the tabs
|
||||||
|
do {
|
||||||
|
val luaBios = ItemStack(Items.EE0.get())
|
||||||
|
val res = Minecraft.getInstance().resourceManager.getResourceOrThrow(
|
||||||
|
ResourceLocation.fromNamespaceAndPath(
|
||||||
|
NeoComputers.MODID,
|
||||||
|
"lua/oc_bios.lua"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val stream = res.openAsReader()
|
||||||
|
val code = stream.readText().encodeToByteArray()
|
||||||
|
stream.close()
|
||||||
|
val codeBuf = ByteBuffer.allocate(code.size)
|
||||||
|
codeBuf.put(code)
|
||||||
|
luaBios.set(DataComponents.LABEL, "Lua BIOS")
|
||||||
|
luaBios.set(DataComponents.ARCH, "Lua 5.2")
|
||||||
|
luaBios.set(DataComponents.EEPROM_CODE, codeBuf)
|
||||||
|
luaBios.set(DataComponents.EEPROM_CODESIZE, code.size)
|
||||||
|
output.accept(luaBios)
|
||||||
|
} while(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/main/kotlin/org/neoflock/neocomputers/item/TunnelCard.kt
Normal file
39
src/main/kotlin/org/neoflock/neocomputers/item/TunnelCard.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package org.neoflock.neocomputers.item
|
||||||
|
|
||||||
|
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.ComponentUser
|
||||||
|
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||||
|
|
||||||
|
class TunnelCard: Item(Properties().component(DataComponents.TUNNEL_CHANNEL, "creative")), ComponentItem {
|
||||||
|
// yes, we're counting TUNNEL as a conventional networking card
|
||||||
|
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.CARD, ComponentRoles.NETWORK)
|
||||||
|
|
||||||
|
override fun getComponentTier(itemStack: ItemStack): Int = 3
|
||||||
|
|
||||||
|
override fun whenComponentPlaced(itemStack: ItemStack, machine: ComponentUser?, newRole: String) {
|
||||||
|
if(machine != null) ensureHasAddress(itemStack)
|
||||||
|
super.whenComponentPlaced(itemStack, machine, newRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Tunnel Component
|
||||||
|
override fun toComponentNode(itemStack: ItemStack, machine: ComponentUser?) = null
|
||||||
|
|
||||||
|
override fun appendHoverText(
|
||||||
|
itemStack: ItemStack,
|
||||||
|
tooltipContext: TooltipContext,
|
||||||
|
list: MutableList<Component?>,
|
||||||
|
tooltipFlag: TooltipFlag
|
||||||
|
) {
|
||||||
|
if(tooltipFlag.isAdvanced) {
|
||||||
|
val addr = itemStack.get(DataComponents.ADDRESS)
|
||||||
|
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
|
||||||
|
list.addLast(addrComp)
|
||||||
|
list.addLast(Component.translatable("neocomputers.tunnel.channel", itemStack.get(DataComponents.TUNNEL_CHANNEL) ?: "creative"))
|
||||||
|
// TODO: show max packet size and whatnot
|
||||||
|
}
|
||||||
|
super.appendHoverText(itemStack, tooltipContext, list, tooltipFlag)
|
||||||
|
}
|
||||||
|
}
|
||||||
256
src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt
Normal file
256
src/main/kotlin/org/neoflock/neocomputers/network/DeviceNode.kt
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
package org.neoflock.neocomputers.network
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.network.Networking.Message
|
||||||
|
import org.neoflock.neocomputers.network.Networking.Visibility
|
||||||
|
import org.neoflock.neocomputers.network.Networking.maxHopCount
|
||||||
|
import java.util.UUID
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
// tmp class until JNI bindings work
|
||||||
|
data class NNComponent(val type: String)
|
||||||
|
|
||||||
|
open class DeviceNode(_address: UUID? = null) {
|
||||||
|
val connections = HashSet<DeviceNode>()
|
||||||
|
private var reachableCache: Set<DeviceNode>? = null
|
||||||
|
var address = _address ?: UUID.randomUUID()
|
||||||
|
|
||||||
|
open var reachability = Visibility.NETWORK
|
||||||
|
open var powerRole = PowerRole.CONSUMER
|
||||||
|
open var energy: Long = 0
|
||||||
|
open var energyCapacity: Long = 0
|
||||||
|
// give energy, returns how much was actually given
|
||||||
|
// cannot exceed amount specified
|
||||||
|
open fun giveEnergy(amount: Long): Long {
|
||||||
|
val maximum = min(amount, energyCapacity - energy)
|
||||||
|
energy += maximum
|
||||||
|
markChanged()
|
||||||
|
return maximum
|
||||||
|
}
|
||||||
|
// take energy out, returns how much was actually taken
|
||||||
|
// cannot exceed amount specified
|
||||||
|
open fun withdrawEnergy(amount: Long): Long {
|
||||||
|
val maximum = min(amount, energy)
|
||||||
|
energy -= maximum
|
||||||
|
markChanged()
|
||||||
|
return maximum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getChargerNodes(): Set<DeviceNode> = getReachable().filter { it.powerRole != PowerRole.CONSUMER }.toSet()
|
||||||
|
fun totalEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energy }
|
||||||
|
fun maxEnergyInConnections(): Long = getChargerNodes().fold(0) { acc, node -> acc + node.energyCapacity }
|
||||||
|
|
||||||
|
// attempts to consume
|
||||||
|
fun consumeEnergy(energy: Long): Boolean {
|
||||||
|
// consumes energy, returns false if not enough
|
||||||
|
val total = totalEnergyInConnections() + this.energy
|
||||||
|
if(energy > total) return false
|
||||||
|
|
||||||
|
var remaining = energy
|
||||||
|
remaining -= withdrawEnergy(remaining)
|
||||||
|
if(remaining <= 0) return true
|
||||||
|
|
||||||
|
for (charger in getChargerNodes()) {
|
||||||
|
if(remaining <= 0) break
|
||||||
|
remaining -= charger.withdrawEnergy(remaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PLEASE only call if consumer, in the name of all that is holy
|
||||||
|
fun tryToChargeFully() {
|
||||||
|
var remaining = energyCapacity - energy
|
||||||
|
if(remaining <= 0) return
|
||||||
|
for (charger in getChargerNodes()) {
|
||||||
|
if(remaining <= 0) break
|
||||||
|
val amount = charger.withdrawEnergy(remaining)
|
||||||
|
val given = giveEnergy(amount)
|
||||||
|
remaining -= given
|
||||||
|
if(given < amount) {
|
||||||
|
val delta = amount - given // amount lost while given back
|
||||||
|
if(charger.giveEnergy(delta) < delta) {
|
||||||
|
NeoComputers.LOGGER.warn("LOSING ENERGY! Tried giving $delta back to $charger and we're losing our marbles!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only call if storage
|
||||||
|
fun balanceStorage() {
|
||||||
|
for(battery in getReachable()) {
|
||||||
|
if(battery.powerRole != PowerRole.STORAGE) continue
|
||||||
|
// its so if for example we have a battery with 2x the capacity
|
||||||
|
// we don't try to even the energy between them since that's just bad
|
||||||
|
// and might pointless delete energy over time
|
||||||
|
val capacityRatio = energyCapacity.toDouble() / battery.energyCapacity
|
||||||
|
|
||||||
|
val meaningfulSurplus = (battery.energy * capacityRatio - energy).toLong()
|
||||||
|
|
||||||
|
if(meaningfulSurplus <= 0) {
|
||||||
|
// WE'RE greedy (or negligible surplus)? Do nothing
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// steal from this greedy mf
|
||||||
|
val toSteal = meaningfulSurplus / 2
|
||||||
|
if(toSteal == 0L) continue // broke storahh
|
||||||
|
|
||||||
|
val stolen = battery.withdrawEnergy(toSteal)
|
||||||
|
if(giveEnergy(stolen) < stolen) {
|
||||||
|
NeoComputers.LOGGER.warn("LOSING ENERGY IN NODE $address!!!! THIS IS REALLY BAD!!!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rob the generators
|
||||||
|
fun stealGeneratorPower() {
|
||||||
|
var remaining = energyCapacity - energy
|
||||||
|
|
||||||
|
for(generator in getReachable()) {
|
||||||
|
if(generator.powerRole != PowerRole.GENERATOR) continue
|
||||||
|
// rob this mf
|
||||||
|
val robbed = generator.withdrawEnergy(remaining)
|
||||||
|
val taken = giveEnergy(robbed)
|
||||||
|
if(taken < robbed) {
|
||||||
|
NeoComputers.LOGGER.warn("energy caught being DELETED in the big 26")
|
||||||
|
}
|
||||||
|
remaining -= taken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun tick() {
|
||||||
|
if(powerRole == PowerRole.CONSUMER) tryToChargeFully()
|
||||||
|
if(powerRole == PowerRole.STORAGE) {
|
||||||
|
stealGeneratorPower()
|
||||||
|
balanceStorage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// processes a received message
|
||||||
|
open fun received(message: Message) {}
|
||||||
|
|
||||||
|
// called when a new direct connection is made
|
||||||
|
open fun onConnect(deviceNode: DeviceNode) {}
|
||||||
|
// called when a direct connection is lost
|
||||||
|
open fun onDisconnect(deviceNode: DeviceNode) {}
|
||||||
|
|
||||||
|
// called when a new node is added globally
|
||||||
|
open fun onNodeAdded(deviceNode: DeviceNode) {
|
||||||
|
invalidateReachableCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
// called when a node is removed globally
|
||||||
|
open fun onNodeRemoved(deviceNode: DeviceNode) {
|
||||||
|
invalidateReachableCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getReachable(): Set<DeviceNode> {
|
||||||
|
if(reachableCache == null) {
|
||||||
|
reachableCache = computeReachable()
|
||||||
|
}
|
||||||
|
return reachableCache!!
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun invalidateReachableCache() {
|
||||||
|
reachableCache = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a subset of connections, for a subset of direct
|
||||||
|
// meant for things like drives which dont want to accidentally fuse networks
|
||||||
|
open fun getPreferredFew() = setOf<DeviceNode>()
|
||||||
|
|
||||||
|
fun computeReachable(): Set<DeviceNode> {
|
||||||
|
if(reachability == Visibility.NONE) {
|
||||||
|
return setOf()
|
||||||
|
}
|
||||||
|
if(reachability == Visibility.SOME) {
|
||||||
|
return getPreferredFew()
|
||||||
|
}
|
||||||
|
if(reachability == Visibility.NETWORK) {
|
||||||
|
// absolute cinema
|
||||||
|
val working = HashSet<DeviceNode>()
|
||||||
|
val pending = mutableListOf(this)
|
||||||
|
var iterCount = 0
|
||||||
|
while(iterCount < maxHopCount && pending.isNotEmpty()) {
|
||||||
|
iterCount++
|
||||||
|
val subnode = pending.removeFirst()
|
||||||
|
if(subnode in working) continue
|
||||||
|
working.add(subnode)
|
||||||
|
if(subnode.reachability == Visibility.NETWORK) {
|
||||||
|
pending.addAll(subnode.connections)
|
||||||
|
} else if(subnode.reachability == Visibility.SOME) {
|
||||||
|
pending.addAll(subnode.getPreferredFew())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// cannot send to itself!
|
||||||
|
working.remove(this)
|
||||||
|
return working
|
||||||
|
}
|
||||||
|
throw NotImplementedError("visibility not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connectTo(other: DeviceNode) {
|
||||||
|
this.directConnectTo(other)
|
||||||
|
other.directConnectTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disconnectFrom(other: DeviceNode) {
|
||||||
|
this.directDisconnectFrom(other)
|
||||||
|
other.directDisconnectFrom(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun directConnectTo(other: DeviceNode) {
|
||||||
|
if(other == this) return
|
||||||
|
if(other in connections) return
|
||||||
|
connections.add(other)
|
||||||
|
this.onConnect(other)
|
||||||
|
invalidateReachableCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun directDisconnectFrom(other: DeviceNode) {
|
||||||
|
if(other !in connections) return
|
||||||
|
connections.remove(other)
|
||||||
|
this.onDisconnect(other)
|
||||||
|
invalidateReachableCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network synchronization with the NodeSynchronizer
|
||||||
|
// TODO: process shi
|
||||||
|
|
||||||
|
var outOfSync = false
|
||||||
|
fun markChanged() {
|
||||||
|
outOfSync = true
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {}
|
||||||
|
open fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) {}
|
||||||
|
|
||||||
|
// Meant to write the entire state as a single commit
|
||||||
|
// for clients which say they have no fucking idea what the server is storing
|
||||||
|
open fun writeFullStateCommit(buf: FriendlyByteBuf) {}
|
||||||
|
|
||||||
|
// client-side, meant to bring state forward
|
||||||
|
open fun processCommit(buf: FriendlyByteBuf) {}
|
||||||
|
|
||||||
|
open fun getComponent(): NNComponent? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by the relay
|
||||||
|
// If the ComponentItem in the card slot
|
||||||
|
interface ConventionalNetworkDevice {
|
||||||
|
fun sendClassicPacket(packet: Networking.ClassicPacket)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class WirelessEndpoint(address: UUID?) : DeviceNode(address) {
|
||||||
|
|
||||||
|
abstract fun getEndpointRange(): Double
|
||||||
|
abstract fun getEndpointDimension(): Int
|
||||||
|
abstract fun getEndpointLevel(): Level
|
||||||
|
abstract fun getEndpointPosition(): Vec3
|
||||||
|
// separate from process for simplicity
|
||||||
|
abstract fun receiveWireless(message: Message, emitter: WirelessEndpoint)
|
||||||
|
}
|
||||||
161
src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt
Normal file
161
src/main/kotlin/org/neoflock/neocomputers/network/Networking.kt
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
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
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
enum class PowerRole {
|
||||||
|
// consumes energy, wants to be fully charged
|
||||||
|
// does not give energy to network nodes
|
||||||
|
CONSUMER,
|
||||||
|
// stores energy, will not care to charge itself
|
||||||
|
// will happily give energy to network nodes
|
||||||
|
STORAGE,
|
||||||
|
// only produces energy, thus obviously charges itself
|
||||||
|
// also happily gives energy
|
||||||
|
GENERATOR,
|
||||||
|
}
|
||||||
|
|
||||||
|
object Networking {
|
||||||
|
// maximum amount of hops between nodes
|
||||||
|
var maxHopCount = 32
|
||||||
|
var tickCount = 0
|
||||||
|
|
||||||
|
enum class Visibility {
|
||||||
|
// only it can see itself
|
||||||
|
NONE,
|
||||||
|
// some, as determined by getPreferredFew()
|
||||||
|
SOME,
|
||||||
|
// Can see everything network-wide
|
||||||
|
NETWORK,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Message(val sender: DeviceNode)
|
||||||
|
|
||||||
|
// null dst means broadcast
|
||||||
|
class ClassicPacket(sender: DeviceNode, val src: String, val dst: String?, val port: Int, val data: List<Any>, val hopCount: Int) : Message(sender) {
|
||||||
|
fun hop(sender: DeviceNode) = ClassicPacket(sender, src, dst, port, data, hopCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for plugins and shi
|
||||||
|
class ComputerCheckedSignal(sender: DeviceNode, val player: String?, val name: String, val data: Array<Any>): Message(sender)
|
||||||
|
class ComputerUncheckedSignal(sender: DeviceNode, val name: String, val data: Array<Any>): Message(sender)
|
||||||
|
class ComputerEvent(sender: DeviceNode, val machineEvent: MachineEvent): Message(sender)
|
||||||
|
|
||||||
|
val wirelessNodes = ThreadLocal.withInitial { HashSet<WirelessEndpoint>() }
|
||||||
|
val allNodes = ThreadLocal.withInitial { HashMap<UUID, DeviceNode>() }
|
||||||
|
|
||||||
|
// node may differ from message.sender in the case of relays,
|
||||||
|
// as they might have DIRECT reachability but
|
||||||
|
fun emitMessage(deviceNode: DeviceNode, message: Message, exclude: Set<DeviceNode> = setOf()) {
|
||||||
|
deviceNode.getReachable().forEach { if(it !in exclude) it.received(message) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeRangeAllowedByHardness(level: Level, src: BlockPos, dst: BlockPos): Double {
|
||||||
|
return Double.POSITIVE_INFINITY // TODO: math
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emitWirelessMessage(starter: WirelessEndpoint, range: Double, message: Message) {
|
||||||
|
val startPos = starter.getEndpointPosition()
|
||||||
|
val startDim = starter.getEndpointDimension()
|
||||||
|
val level = starter.getEndpointLevel()
|
||||||
|
wirelessNodes.get().forEach {
|
||||||
|
if(it.getEndpointDimension() != startDim) return
|
||||||
|
val pos = it.getEndpointPosition()
|
||||||
|
val d = startPos.distanceTo(pos)
|
||||||
|
var trueRange = min(it.getEndpointRange(), range)
|
||||||
|
trueRange = min(trueRange, computeRangeAllowedByHardness(level, BlockPos.containing(startPos), BlockPos.containing(pos)))
|
||||||
|
if(d > trueRange) return
|
||||||
|
it.receiveWireless(message, starter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tickAllNodes() {
|
||||||
|
allNodes.get().forEach { it.value.tick() }
|
||||||
|
tickCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNode(address: UUID): DeviceNode? = allNodes.get()[address]
|
||||||
|
|
||||||
|
// TODO: use setter, more convenient
|
||||||
|
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) {
|
||||||
|
if(deviceNode.address in allNodes.get()) return
|
||||||
|
allNodes.get()[deviceNode.address] = deviceNode
|
||||||
|
if(deviceNode is WirelessEndpoint) {
|
||||||
|
wirelessNodes.get().add(deviceNode);
|
||||||
|
}
|
||||||
|
// notify at the end so it is notified of its own creation
|
||||||
|
allNodes.get().forEach { it.value.onNodeAdded(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 {
|
||||||
|
deviceNode.disconnectFrom(it)
|
||||||
|
}
|
||||||
|
// actually remove at the end so it can listen to its own removal
|
||||||
|
allNodes.get().remove(deviceNode.address)
|
||||||
|
if(deviceNode is WirelessEndpoint) {
|
||||||
|
wirelessNodes.get().remove(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) {
|
||||||
|
val localChannels = channels.get()
|
||||||
|
if(!localChannels.containsKey(channel)) {
|
||||||
|
localChannels[channel] = mutableSetOf();
|
||||||
|
}
|
||||||
|
localChannels[channel]!!.add(deviceNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromChannel(channel: String, deviceNode: DeviceNode) {
|
||||||
|
val localChannels = channels.get()
|
||||||
|
if(!localChannels.containsKey(channel)) return;
|
||||||
|
localChannels[channel]?.remove(deviceNode);
|
||||||
|
if(localChannels[channel].isNullOrEmpty()) {
|
||||||
|
localChannels.remove(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emitChannelMessage(starter: DeviceNode, channel: String, message: Message) {
|
||||||
|
val localChannels = channels.get()
|
||||||
|
val c = localChannels[channel] ?: return;
|
||||||
|
c.forEach { if(it != starter) it.received(message); };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +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 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: DeviceBlockEntity> registerPowerDevice(blockEntityType: BlockEntityType<T>) {
|
||||||
|
//? if fabric {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, blockEntityType);
|
||||||
|
//?}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.neoflock.neocomputers.sounds
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance
|
||||||
|
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
|
||||||
|
import net.minecraft.client.resources.sounds.MinecartSoundInstance
|
||||||
|
import net.minecraft.client.resources.sounds.SoundInstance
|
||||||
|
import net.minecraft.sounds.SoundEvent
|
||||||
|
import net.minecraft.sounds.SoundSource
|
||||||
|
import org.neoflock.neocomputers.entity.MachineEntity
|
||||||
|
|
||||||
|
class ComputerRunningSoundInstance: AbstractTickableSoundInstance {
|
||||||
|
val machine: MachineEntity
|
||||||
|
|
||||||
|
fun updatePosition() {
|
||||||
|
val pos = machine.getMachineBlockPosition()
|
||||||
|
this.x = pos.x.toDouble() + 0.5
|
||||||
|
this.y = pos.y.toDouble() + 0.5
|
||||||
|
this.z = pos.z.toDouble() + 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(machine: MachineEntity, soundEvent: SoundEvent, soundSource: SoundSource): super(soundEvent, soundSource, SoundInstance.createUnseededRandom()) {
|
||||||
|
this.machine = machine
|
||||||
|
this.looping = true
|
||||||
|
this.delay = 0
|
||||||
|
this.volume = 1.0F
|
||||||
|
this.pitch = 1.0F
|
||||||
|
updatePosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
if(!machine.isRunning()) {
|
||||||
|
this.stop()
|
||||||
|
} else {
|
||||||
|
updatePosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt
Normal file
163
src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package org.neoflock.neocomputers.sounds
|
||||||
|
|
||||||
|
import dev.architectury.registry.registries.DeferredRegister
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.core.registries.Registries
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.sounds.SoundEvent
|
||||||
|
import net.minecraft.world.phys.Vec3
|
||||||
|
import org.lwjgl.BufferUtils
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.lwjgl.openal.AL10
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import kotlin.experimental.xor
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.sign
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
|
object Sounds {
|
||||||
|
val SOUNDS = DeferredRegister.create(NeoComputers.MODID, Registries.SOUND_EVENT)!!
|
||||||
|
|
||||||
|
val COMPUTER_RUNNING = registerSound("computer_running")
|
||||||
|
|
||||||
|
fun registerSound(name: String) = SOUNDS.register(name) {
|
||||||
|
SoundEvent.createVariableRangeEvent(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, name))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
val BEEP_SAMPLERATE = 44100
|
||||||
|
val BEEP_AMPLITUDE = 32f
|
||||||
|
val BEEP_MAXDIST = 16f
|
||||||
|
|
||||||
|
// Also largely taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala
|
||||||
|
|
||||||
|
val allSounds = ThreadLocal.withInitial { mutableListOf<CustomSoundBuffer>() }
|
||||||
|
|
||||||
|
class CustomSoundBuffer {
|
||||||
|
var dead: Boolean = true
|
||||||
|
var buffer: Int = -1
|
||||||
|
var source: Int = -1
|
||||||
|
|
||||||
|
fun start(x: Float, y: Float, z: Float, data: ByteBuffer, gain: Float): Int? {
|
||||||
|
// clear errors or smth idk
|
||||||
|
AL10.alGetError()
|
||||||
|
|
||||||
|
// written in a C style by a C dev
|
||||||
|
// all this work on a JVM project and I'm still writing C
|
||||||
|
// would be better if Kotlin had goto btw just saying
|
||||||
|
val ok = AL10.AL_NO_ERROR
|
||||||
|
var err = ok
|
||||||
|
buffer = AL10.alGenBuffers()
|
||||||
|
err = AL10.alGetError()
|
||||||
|
if(err != ok) return err
|
||||||
|
|
||||||
|
AL10.alBufferData(buffer, AL10.AL_FORMAT_MONO8, data, BEEP_SAMPLERATE)
|
||||||
|
err = AL10.alGetError()
|
||||||
|
if(err != ok) {
|
||||||
|
AL10.alDeleteBuffers(buffer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source = AL10.alGenSources()
|
||||||
|
err = AL10.alGetError()
|
||||||
|
if(err != ok) {
|
||||||
|
AL10.alDeleteBuffers(buffer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
AL10.alSourceQueueBuffers(source, buffer)
|
||||||
|
err = AL10.alGetError()
|
||||||
|
if(err != ok) {
|
||||||
|
AL10.alDeleteBuffers(buffer)
|
||||||
|
AL10.alDeleteSources(source)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
AL10.alSource3f(source, AL10.AL_POSITION, x, y, z)
|
||||||
|
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, BEEP_MAXDIST)
|
||||||
|
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, BEEP_MAXDIST)
|
||||||
|
AL10.alSourcef(source, AL10.AL_GAIN, gain * 0.3f)
|
||||||
|
err = AL10.alGetError()
|
||||||
|
if(err != ok) {
|
||||||
|
AL10.alDeleteBuffers(buffer)
|
||||||
|
AL10.alDeleteSources(source)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
AL10.alSourcePlay(source)
|
||||||
|
err = AL10.alGetError()
|
||||||
|
if(err != ok) {
|
||||||
|
AL10.alDeleteBuffers(buffer)
|
||||||
|
AL10.alDeleteSources(source)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dead = false
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkDone(): Boolean {
|
||||||
|
if(dead) return true
|
||||||
|
if(AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) return false
|
||||||
|
dead = true
|
||||||
|
AL10.alDeleteSources(source)
|
||||||
|
AL10.alDeleteBuffers(buffer)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun beep(pos: Vec3, pattern: String, frequency: Int = 1000, duration: Int = 200) {
|
||||||
|
NeoComputers.LOGGER.info("Beep: $pattern, $frequency Hz, $duration ms")
|
||||||
|
val mc = Minecraft.getInstance()
|
||||||
|
val playerPos = mc.player?.position() ?: pos
|
||||||
|
val distanceBasedGain = max(0.0, 1 - pos.distanceTo(playerPos) / BEEP_MAXDIST).toFloat()
|
||||||
|
val volume = 1.0
|
||||||
|
val gain = distanceBasedGain * volume
|
||||||
|
if (gain <= 0 || BEEP_AMPLITUDE <= 0) return
|
||||||
|
|
||||||
|
// Algorithm effectively ported over from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala
|
||||||
|
// We do add support for spaces tho
|
||||||
|
val charArr = pattern.toCharArray()
|
||||||
|
val sampleCounts = charArr.map { if(it == '.') duration else 2 * duration }.map { it * BEEP_SAMPLERATE / 1000 }
|
||||||
|
val pauseSample = 50 * BEEP_SAMPLERATE / 1000
|
||||||
|
|
||||||
|
val finalBuf = BufferUtils.createByteBuffer(sampleCounts.sum() + pauseSample * sampleCounts.lastIndex)
|
||||||
|
val step = frequency.toFloat() / BEEP_SAMPLERATE
|
||||||
|
var off = 0f
|
||||||
|
for((i, sampleCount) in sampleCounts.withIndex()) {
|
||||||
|
if(charArr[i] == ' ') {
|
||||||
|
for(sample in 0..<sampleCount) {
|
||||||
|
finalBuf.put(127)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(sample in 0..<sampleCount) {
|
||||||
|
val angle = 2 * PI * off
|
||||||
|
val value = (sin(angle).sign * BEEP_AMPLITUDE).toInt().toByte().xor(0x80.toByte())
|
||||||
|
off += step
|
||||||
|
if(off > 1) off -= 1f
|
||||||
|
finalBuf.put(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(finalBuf.hasRemaining()) {
|
||||||
|
for(sample in 0..<pauseSample) {
|
||||||
|
finalBuf.put(127)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalBuf.rewind()
|
||||||
|
|
||||||
|
val sound = CustomSoundBuffer()
|
||||||
|
val soundErr = sound.start(pos.x.toFloat(), pos.y.toFloat(), pos.z.toFloat(), finalBuf, gain.toFloat())
|
||||||
|
if(soundErr != null) {
|
||||||
|
NeoComputers.LOGGER.error("Playing beep failed, OpenAL exit code of $soundErr")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoComputers.LOGGER.info("Beeping with ${finalBuf.capacity()} samples")
|
||||||
|
allSounds.get().addLast(sound)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tickCustomSounds() {
|
||||||
|
allSounds.get().removeIf { it.checkDone() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.neoflock.neocomputers.utils
|
||||||
|
|
||||||
|
import dev.architectury.registry.fuel.FuelRegistry
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
|
||||||
|
// mewhenthe, aka e, will have me publicly executed for this code
|
||||||
|
object ContainerUtils {
|
||||||
|
fun getBurningTime(itemStack: ItemStack): Int? {
|
||||||
|
val time = FuelRegistry.get(itemStack)
|
||||||
|
if(time == 0) return null
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isBurningFuel(itemStack: ItemStack) = getBurningTime(itemStack) != null
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package org.neoflock.neocomputers.utils;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.server.packs.resources.Resource
|
||||||
|
import net.minecraft.server.packs.resources.ResourceManager
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OC hex font format:
|
||||||
|
* 5 character hex code .. ":" .. variable length hex code .. LF
|
||||||
|
* this is essentially a dictionary
|
||||||
|
* */
|
||||||
|
object FontProvider {
|
||||||
|
val map: MutableMap<Char, ArrayList<Byte>> = mutableMapOf();
|
||||||
|
|
||||||
|
fun load(loc: ResourceLocation) {
|
||||||
|
var man: ResourceManager = Minecraft.getInstance().resourceManager
|
||||||
|
var resource: Resource = man.getResourceOrThrow(loc)
|
||||||
|
var stream = resource.open()
|
||||||
|
var bfr = stream.bufferedReader();
|
||||||
|
while (stream.available() > 0) {
|
||||||
|
var line: String = bfr.readLine()
|
||||||
|
var splitLine = line.split(":");
|
||||||
|
var key = splitLine[0].hexToInt().toChar();
|
||||||
|
var value: ByteArray = splitLine[1].hexToByteArray(); // shout out to the kotlin stdlib for having ts
|
||||||
|
var bytes: ArrayList<Byte> = value.toCollection(ArrayList<Byte>());
|
||||||
|
map[key] = bytes
|
||||||
|
}
|
||||||
|
NeoComputers.LOGGER.info("[FontProvider] Loaded font!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.neoflock.neocomputers.utils
|
||||||
|
|
||||||
|
object Formatting {
|
||||||
|
fun formatMemory(size: Long, spacing: String = " "): String {
|
||||||
|
var unit = 0
|
||||||
|
val units = listOf("B", "KiB", "MiB", "GiB", "TiB", "PiB")
|
||||||
|
var num = size.toDouble()
|
||||||
|
while(unit < units.lastIndex && num >= 1024) {
|
||||||
|
num /= 1024
|
||||||
|
unit++
|
||||||
|
}
|
||||||
|
num = (num * 100).toInt().toDouble() / 100
|
||||||
|
return "$num$spacing${units[unit]}"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
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
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.server.packs.resources.Resource
|
||||||
|
import net.minecraft.world.ContainerHelper
|
||||||
|
import net.minecraft.world.entity.player.Inventory
|
||||||
|
import net.minecraft.world.entity.player.Player
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
|
import net.minecraft.world.inventory.MenuType
|
||||||
|
import net.minecraft.world.inventory.Slot
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
|
import org.neoflock.neocomputers.NeoComputers
|
||||||
|
import org.neoflock.neocomputers.gui.widget.DynamicSlot
|
||||||
|
|
||||||
|
// Common container interface, assumes the entire purpose is purely raw item storage
|
||||||
|
interface GenericContainer : Container {
|
||||||
|
fun getItems(): NonNullList<ItemStack>
|
||||||
|
|
||||||
|
override fun getContainerSize(): Int {
|
||||||
|
return getItems().size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return getItems().all { it.isEmpty }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(i: Int): ItemStack {
|
||||||
|
return getItems()[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItem(slot: Int, count: Int): ItemStack {
|
||||||
|
val res = ContainerHelper.removeItem(getItems(), slot, count)
|
||||||
|
if (!res.isEmpty) setChanged()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setItem(slot: Int, itemStack: ItemStack) {
|
||||||
|
getItems()[slot] = itemStack
|
||||||
|
|
||||||
|
// in case of bullshit
|
||||||
|
if(itemStack.count > itemStack.maxStackSize) {
|
||||||
|
// rip items
|
||||||
|
itemStack.count = itemStack.maxStackSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeItemNoUpdate(i: Int): ItemStack {
|
||||||
|
return ContainerHelper.takeItem(getItems(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearContent() {
|
||||||
|
getItems().clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var container: Container): AbstractContainerMenu(menuType, id) {
|
||||||
|
fun addInventorySlots(inventory: Inventory, x: Int, y: Int) {
|
||||||
|
// 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, y + i * 18))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addInventoryHotbar(inventory, x, y + 3 * 18 + 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addInventoryHotbar(inventory: Inventory, x: Int, y: Int) {
|
||||||
|
for (i in 0..8) {
|
||||||
|
this.addSlot(Slot(inventory, i, x + i * 18, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from https://docs.fabricmc.net/develop/blocks/container-menus
|
||||||
|
override fun quickMoveStack(player: Player, i: Int): ItemStack? {
|
||||||
|
val slot = slots[i]
|
||||||
|
|
||||||
|
if(!slot.hasItem()) return ItemStack.EMPTY
|
||||||
|
|
||||||
|
val stack = slot.item
|
||||||
|
val copied = stack.copy()
|
||||||
|
val contSize = container.containerSize
|
||||||
|
|
||||||
|
if(i < contSize) {
|
||||||
|
if(!this.moveItemStackTo(stack, contSize, slots.size, true)) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
} else if(!this.moveItemStackTo(stack, 0, contSize, false)) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stack.isEmpty) {
|
||||||
|
slot.setByPlayer(ItemStack.EMPTY)
|
||||||
|
} else {
|
||||||
|
slot.setChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
return copied
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stillValid(player: Player): Boolean {
|
||||||
|
return container.stillValid(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class GenericContainerScreen<T: GenericContainerMenu>(menu: T, inventory: Inventory, component: Component): AbstractContainerScreen<T>(menu, inventory, component) {
|
||||||
|
open fun shouldCenterTitle() = true
|
||||||
|
open fun shouldRenderTooltip() = true
|
||||||
|
open fun findMenuTexture(): ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/background.png")
|
||||||
|
|
||||||
|
open fun getBoundBlockEntityType(): Set<BlockEntityType<*>> = setOf()
|
||||||
|
|
||||||
|
open fun processScreenStatePacket(buf: FriendlyByteBuf) {}
|
||||||
|
|
||||||
|
val imageX: Int
|
||||||
|
get() = (width - imageWidth) / 2
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
if(shouldCenterTitle()) this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderBg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {
|
||||||
|
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, 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) {
|
||||||
|
slot.draw(guiGraphics, i-cx, j-cy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guiGraphics.pose().popPose()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package org.neoflock.neocomputers.utils
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
|
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 blank = GPUChar(' ')
|
||||||
|
var buf = Array(width*height) { blank }
|
||||||
|
|
||||||
|
fun encodeContents(buf: FriendlyByteBuf) {
|
||||||
|
// 0x01 means set fg, 0x02 means set bg,
|
||||||
|
// 0x03 means set char+count
|
||||||
|
var lastFg = 0xFFFFFF
|
||||||
|
var lastBg = 0x000000
|
||||||
|
|
||||||
|
buf.writeVarInt(width)
|
||||||
|
buf.writeVarInt(height)
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while(i < this.buf.size) {
|
||||||
|
val px = this.buf[i]
|
||||||
|
if(px.fg != lastFg) {
|
||||||
|
buf.writeByte(0x01)
|
||||||
|
buf.writeVarInt(px.fg)
|
||||||
|
lastFg = px.fg
|
||||||
|
}
|
||||||
|
if(px.bg != lastBg) {
|
||||||
|
buf.writeByte(0x02)
|
||||||
|
buf.writeVarInt(px.bg)
|
||||||
|
lastBg = px.bg
|
||||||
|
}
|
||||||
|
var charWritten = 1
|
||||||
|
while((i+charWritten) < this.buf.size && this.buf[i+charWritten].c == px.c) charWritten++
|
||||||
|
buf.writeByte(0x03)
|
||||||
|
buf.writeVarInt(px.c.code)
|
||||||
|
buf.writeVarInt(charWritten)
|
||||||
|
i += charWritten
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decodeContents(buf: FriendlyByteBuf) {
|
||||||
|
var lastFg = 0xFFFFFF
|
||||||
|
var lastBg = 0x000000
|
||||||
|
|
||||||
|
width = buf.readVarInt()
|
||||||
|
height = buf.readVarInt()
|
||||||
|
|
||||||
|
if(width*height != this.buf.size) {
|
||||||
|
this.buf = Array(width * height) { blank }
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while(i < width*height) {
|
||||||
|
val op = buf.readByte().toInt()
|
||||||
|
if(op == 0x01) {
|
||||||
|
lastFg = buf.readVarInt()
|
||||||
|
}
|
||||||
|
if(op == 0x02) {
|
||||||
|
lastBg = buf.readVarInt()
|
||||||
|
}
|
||||||
|
if(op == 0x03) {
|
||||||
|
val c = buf.readVarInt().toChar()
|
||||||
|
val n = buf.readVarInt()
|
||||||
|
|
||||||
|
for(o in 0..<n) {
|
||||||
|
this.buf[i+o] = GPUChar(c, lastFg, lastBg)
|
||||||
|
}
|
||||||
|
i += n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inBounds(x: Int, y: Int) = x >= 0 && y >= 0 && x < width && y < height
|
||||||
|
fun get(x: Int, y: Int) = if(inBounds(x, y)) buf[x+y*width] else blank
|
||||||
|
fun set(x: Int, y: Int, pixel: GPUChar) {
|
||||||
|
if(!inBounds(x, y)) return
|
||||||
|
buf[x+y*width] = pixel
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun fill(x: Int, y: Int, w: Int, h: Int, pixel: GPUChar = blank) {
|
||||||
|
// turn it into values we can fw
|
||||||
|
val fw = min(w, width)
|
||||||
|
val fh = min(h, height)
|
||||||
|
for(py in y..<y+fh) {
|
||||||
|
for (px in x..<x + fw) {
|
||||||
|
set(px, py, pixel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/main/resources/assets/neocomputers/atlases/gui.json
Normal file
8
src/main/resources/assets/neocomputers/atlases/gui.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "single",
|
||||||
|
"source": "neocomputers:block/relay_top"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"multipart": [
|
||||||
|
{ "apply": { "model": "neocomputers:block/cable/cable_center" }},
|
||||||
|
{ "when": { "south": true },
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_connection" }},
|
||||||
|
{ "when": { "east": true},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": -90}},
|
||||||
|
{ "when": { "west": true },
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": 90}},
|
||||||
|
{ "when": { "north": true },
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": 180}},
|
||||||
|
{ "when": { "up": true },
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_connection", "x": 90}},
|
||||||
|
{ "when": { "down": true },
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_connection", "x": -90}},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"south": false,
|
||||||
|
"north": false,
|
||||||
|
"east": false,
|
||||||
|
"west": false,
|
||||||
|
"up": false,
|
||||||
|
"down": false
|
||||||
|
},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_caps" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"south": true,
|
||||||
|
"north": false,
|
||||||
|
"east": false,
|
||||||
|
"west": false,
|
||||||
|
"up": false,
|
||||||
|
"down": false
|
||||||
|
},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_cap" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"south": false,
|
||||||
|
"north": true,
|
||||||
|
"east": false,
|
||||||
|
"west": false,
|
||||||
|
"up": false,
|
||||||
|
"down": false
|
||||||
|
},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": 180 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"south": false,
|
||||||
|
"north": false,
|
||||||
|
"east": true,
|
||||||
|
"west": false,
|
||||||
|
"up": false,
|
||||||
|
"down": false
|
||||||
|
},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": -90 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"south": false,
|
||||||
|
"north": false,
|
||||||
|
"east": false,
|
||||||
|
"west": true,
|
||||||
|
"up": false,
|
||||||
|
"down": false
|
||||||
|
},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": 90 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"south": false,
|
||||||
|
"north": false,
|
||||||
|
"east": false,
|
||||||
|
"west": false,
|
||||||
|
"up": true,
|
||||||
|
"down": false
|
||||||
|
},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_cap", "x": 90 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"when": {
|
||||||
|
"south": false,
|
||||||
|
"north": false,
|
||||||
|
"east": false,
|
||||||
|
"west": false,
|
||||||
|
"up": false,
|
||||||
|
"down": true
|
||||||
|
},
|
||||||
|
"apply": { "model": "neocomputers:block/cable/cable_cap", "x": -90 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": {
|
||||||
|
"model": "neocomputers:block/capacitor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": {
|
||||||
|
"model": "neocomputers:block/capacitor2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": {
|
||||||
|
"model": "neocomputers:block/capacitor3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/main/resources/assets/neocomputers/blockstates/case.json
Normal file
19
src/main/resources/assets/neocomputers/blockstates/case.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"facing=north": {
|
||||||
|
"model": "neocomputers:block/case"
|
||||||
|
},
|
||||||
|
"facing=east": {
|
||||||
|
"model": "neocomputers:block/case",
|
||||||
|
"y": 90
|
||||||
|
},
|
||||||
|
"facing=west": {
|
||||||
|
"model": "neocomputers:block/case",
|
||||||
|
"y": -90
|
||||||
|
},
|
||||||
|
"facing=south": {
|
||||||
|
"model": "neocomputers:block/case",
|
||||||
|
"y": 180
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user