Compare commits
161 Commits
6509d4d94f
...
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 | |||
| e21ee3057a | |||
| 60ca91c79c | |||
| 2328169f2a | |||
|
|
cf960ba22c | ||
|
|
9dd6fccedf | ||
|
|
abfad91673 | ||
|
|
b629298438 | ||
|
|
7281535fad | ||
|
|
65ed8951fc | ||
|
|
81ac4181ae | ||
|
|
20d891079b | ||
|
|
6a31c4ef11 |
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$" }
|
||||
|
||||
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -4,7 +4,8 @@
|
||||
# against bad commits.
|
||||
|
||||
name: build
|
||||
on: [pull_request, push]
|
||||
#on: [pull_request, push]
|
||||
on: []
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -451,4 +451,11 @@ FodyWeavers.xsd
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
*.sln.iml
|
||||
/.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
|
||||
601
LICENSE
601
LICENSE
@@ -1,121 +1,504 @@
|
||||
Creative Commons Legal Code
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
CC0 1.0 Universal
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Statement of Purpose
|
||||
Preamble
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random
|
||||
Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
36
README.md
36
README.md
@@ -1,28 +1,14 @@
|
||||
# Stonecutter template
|
||||
# NeoComputers
|
||||
A rewrite of OpenComputers for modern Minecraft, alongside our own additions. Based off of the https://github.com/JumperOnJava/Stonecutter-Arch-Template template.
|
||||
### Project setup
|
||||
This uses a combination of Architectury, Architectury API, Stonecutter, and Kotlin. Stonecutter is the most important one
|
||||
to read about. Don't forget to Gradle -> Tasks -> stonecutter -> "Set active project to [version]-[loader]" before building/testing
|
||||
the mod for that version!
|
||||
|
||||
This template allows you create multiloader multitemplate mod using stonecutter and architectury
|
||||
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.
|
||||
|
||||
It is based on my CustomCursor project
|
||||
The minecraft version this mod is currently being developed on is 1.21.11 neoforge or fabric.
|
||||
|
||||
To change versions check settings.gradle.kts
|
||||
|
||||
You can use c# script to automatically change all template names.
|
||||
Open RenameTemplate.cs, change names in replacements array and run "dotnet run" in this directory
|
||||
I would highly recommend to do this before opening project in your IDE, and then remove all c# related files from project
|
||||
(obj and bin folders, .csproj and script itself). Also you can remove c# stuff from .gitignore (there is comment for that)
|
||||
|
||||
If you have some issues with template ping me in [Kiku's realm](https://discord.gg/TBgNUCfryS) or official fabric discord
|
||||
|
||||
Currently supported versions these,
|
||||
but you can easily add other versions if you need that
|
||||
- 1.20.1, fabric, lexforge
|
||||
- 1.20.4, fabric, neoforge
|
||||
- 1.21.1, fabric, neoforge
|
||||
- 1.21.3, fabric, neoforge
|
||||
- 1.21.4, fabric, neoforge
|
||||
|
||||
To start current active version use runActive task
|
||||
|
||||
For testing all versions you can use chiseledRunAllClients, it runs all possible version and loader variants (in random(?) order)
|
||||
|
||||
Also template had publishing set up, you need to specify project id for modrinth and curseforge in gradle.properties, and tokens for these sites in local.properties (it is gitignored, check local.properties.example). After that use chiseledPublishMods task
|
||||
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.
|
||||
@@ -1,60 +0,0 @@
|
||||
List<string> FindAllFiles(string directoryPath)
|
||||
{
|
||||
return Directory.GetFiles(directoryPath, "*.*", SearchOption.AllDirectories)
|
||||
.Select(file => Path.GetRelativePath(directoryPath, file))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var replacements = new[]
|
||||
{
|
||||
//ORDER MATTERS
|
||||
new { Old = "com.example", New = "io.github.cooldev" }, //change mod package
|
||||
new { Old = "template", New = "coolmod" }, //change modid
|
||||
new { Old = "Template", New = "Cool Mod" }, //change mod display name
|
||||
// ↕ <- Letter L is different case here
|
||||
new { Old = "TempLate", New = "CoolMod" }, //change mod init class and other places where mod name is in PascalCase
|
||||
new { Old = "AuthorExample", New = "CoolDev" }
|
||||
};
|
||||
|
||||
var files = FindAllFiles(Directory.GetCurrentDirectory());
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var oldFile = file.Replace("\\", "/");
|
||||
var newFile = oldFile;
|
||||
Console.WriteLine("checking: " + oldFile);
|
||||
|
||||
if (oldFile.StartsWith("."))
|
||||
continue;
|
||||
if (oldFile.Contains(".cs"))
|
||||
continue;
|
||||
if (oldFile.Contains(".git"))
|
||||
continue;
|
||||
if (oldFile.Contains(".gradle/"))
|
||||
continue;
|
||||
if (oldFile.Contains("build/"))
|
||||
continue;
|
||||
if (oldFile.Contains("LICENSE"))
|
||||
continue;
|
||||
if (oldFile.Contains("bin"))
|
||||
continue;
|
||||
if (oldFile.Contains("obj"))
|
||||
continue;
|
||||
|
||||
var fileContent = File.ReadAllText(oldFile);
|
||||
|
||||
foreach (var replacement in replacements)
|
||||
{
|
||||
fileContent = fileContent.Replace(replacement.Old, replacement.New);
|
||||
newFile = newFile.Replace(replacement.Old.Replace(".", "/"), replacement.New.Replace(".", "/"));
|
||||
}
|
||||
Console.WriteLine($"Moving \n\t< {oldFile} \n\t> {newFile}");
|
||||
File.Delete(oldFile);
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(newFile));
|
||||
}
|
||||
catch { }
|
||||
;
|
||||
File.WriteAllText(newFile, fileContent);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<RootNamespace>Stonecutter_Arch_Template</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
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.
|
||||
124
build.gradle.kts
124
build.gradle.kts
@@ -1,15 +1,25 @@
|
||||
import java.util.*
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.3.20"
|
||||
id("dev.architectury.loom")
|
||||
id("architectury-plugin")
|
||||
id("me.modmuss50.mod-publish-plugin")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
val minecraft = stonecutter.current.version
|
||||
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"
|
||||
group = mod.group
|
||||
base {
|
||||
@@ -20,6 +30,7 @@ architectury.common(stonecutter.tree.branches.mapNotNull {
|
||||
if (stonecutter.current.project !in it) null
|
||||
else it.prop("loom.platform")
|
||||
})
|
||||
|
||||
repositories {
|
||||
maven("https://maven.neoforged.net/releases/")
|
||||
|
||||
@@ -27,25 +38,72 @@ repositories {
|
||||
maven("https://maven.terraformersmc.com/")
|
||||
//placeholder api (modmenu depencency)
|
||||
maven("https://maven.nucleoid.xyz/")
|
||||
maven {
|
||||
name = "Kotlin for Forge"
|
||||
setUrl("https://thedarkcolour.github.io/KotlinForForge/")
|
||||
}
|
||||
maven {
|
||||
name = "ParchmentMC"
|
||||
setUrl("https://maven.parchmentmc.org")
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
minecraft("com.mojang:minecraft:$minecraft")
|
||||
// mappings(loom.officialMojangMappings())
|
||||
|
||||
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") {
|
||||
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")
|
||||
modImplementation("com.terraformersmc:modmenu:${mod.dep("modmenu_version")}")
|
||||
|
||||
//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
|
||||
//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")
|
||||
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") {
|
||||
"forge"("net.minecraftforge:forge:${minecraft}-${mod.dep("forge_loader")}")
|
||||
mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||
//implementation("thedarkcolour:kotlinforforge:1.16.0")
|
||||
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||
|
||||
modApi("dev.architectury:architectury-forge:${archversion}")
|
||||
"io.github.llamalad7:mixinextras-forge:${mod.dep("mixin_extras")}".let {
|
||||
implementation(it)
|
||||
include(it)
|
||||
@@ -53,17 +111,26 @@ dependencies {
|
||||
}
|
||||
if (loader == "neoforge") {
|
||||
"neoForge"("net.neoforged:neoforge:${mod.dep("neoforge_loader")}")
|
||||
mappings(loom.layered {
|
||||
mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||
mod.dep("neoforge_patch").takeUnless { it.startsWith('[') }?.let {
|
||||
mappings("dev.architectury:yarn-mappings-patch-neoforge:$it")
|
||||
}
|
||||
})
|
||||
// mappings(loom.layered {
|
||||
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||
// mod.dep("neoforge_patch").takeUnless { it.startsWith('[') }?.let {
|
||||
// mappings("dev.architectury:yarn-mappings-patch-neoforge:$it")
|
||||
// }
|
||||
// })
|
||||
|
||||
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")
|
||||
|
||||
}
|
||||
}
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0")
|
||||
}
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath = rootProject.file("src/main/resources/template.accesswidener")
|
||||
accessWidenerPath = rootProject.file("src/main/resources/neocomputers.accesswidener")
|
||||
|
||||
decompilers {
|
||||
get("vineflower").apply { // Adds names to lambdas - useful for mixins
|
||||
@@ -72,13 +139,12 @@ loom {
|
||||
}
|
||||
if (loader == "forge") {
|
||||
forge.mixinConfigs(
|
||||
"template-common.mixins.json",
|
||||
"template-forge.mixins.json",
|
||||
"neocomputers-common.mixins.json",
|
||||
"neocomputers-forge.mixins.json",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val localProperties = Properties()
|
||||
val localPropertiesFile = rootProject.file("local.properties")
|
||||
if (localPropertiesFile.exists()) {
|
||||
@@ -106,8 +172,12 @@ publishMods {
|
||||
targets.forEach(minecraftVersions::add)
|
||||
if (loader == "fabric") {
|
||||
requires("fabric-api")
|
||||
requires("fabric-language-kotlin")
|
||||
optional("modmenu")
|
||||
}
|
||||
if (loader == "neoforge") {
|
||||
requires("kotlinforforge-neoforge")
|
||||
}
|
||||
}
|
||||
|
||||
curseforge {
|
||||
@@ -116,8 +186,12 @@ publishMods {
|
||||
targets.forEach(minecraftVersions::add)
|
||||
if (loader == "fabric") {
|
||||
requires("fabric-api")
|
||||
requires("fabric-language-kotlin")
|
||||
optional("modmenu")
|
||||
}
|
||||
if (loader == "neoforge") {
|
||||
requires("kotlinforforge-neoforge")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +202,11 @@ java {
|
||||
sourceCompatibility = java
|
||||
}
|
||||
|
||||
kotlin {
|
||||
val java = if (stonecutter.eval(minecraft, ">=1.20.5")) 21 else 17
|
||||
jvmToolchain(java)
|
||||
}
|
||||
|
||||
val shadowBundle: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
@@ -197,3 +276,14 @@ tasks.build {
|
||||
group = "versioned"
|
||||
description = "Must run through 'chiseledBuild'"
|
||||
}
|
||||
if (loader == "fabric") {
|
||||
fabricApi {
|
||||
if (minecraftVersionToNum(minecraft) > minecraftVersionToNum("1.21.4")) {
|
||||
configureDataGeneration() {
|
||||
client = true
|
||||
}
|
||||
} else {
|
||||
configureDataGeneration()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ org.gradle.parallel=false
|
||||
|
||||
# Mod properties
|
||||
mod.version=1.0.0
|
||||
mod.group=com.example
|
||||
mod.id=template
|
||||
mod.name=Template
|
||||
mod.group=org.neoflock.neocomputers
|
||||
mod.id=neocomputers
|
||||
mod.name=NeoComputers
|
||||
|
||||
# Used for the mod metadata
|
||||
mod.mc_dep_fabric=[VERSIONED]
|
||||
@@ -22,7 +22,7 @@ mod.mc_targets=[VERSIONED]
|
||||
|
||||
# Mod setup
|
||||
deps.mixin_extras=0.4.1
|
||||
deps.fabric_loader=0.16.9
|
||||
deps.fabric_loader=0.18.3
|
||||
deps.fabric_version=[VERSIONED]
|
||||
|
||||
deps.forge_loader=[VERSIONED]
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
15
gradlew
vendored
Normal file → Executable file
15
gradlew
vendored
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# 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.
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# 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/.
|
||||
@@ -84,7 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
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.
|
||||
MAX_FD=maximum
|
||||
@@ -112,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -170,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -203,15 +203,14 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * 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" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
5
gradlew.bat
vendored
5
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@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 ##########################################################################
|
||||
@@ -68,11 +70,10 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -21,13 +21,15 @@ stonecutter {
|
||||
fun mc(loader: String, vararg versions: String) {
|
||||
for (version in versions) vers("$version-$loader", version)
|
||||
}
|
||||
//i would recommend to use neoforge for mc > 1.20.1, i haven't tested template for forge on versions higher than that
|
||||
mc("fabric","1.20.1","1.20.4", "1.21.1", "1.21.3", "1.21.4")
|
||||
mc("forge","1.20.1")
|
||||
//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.9", "1.21.11")
|
||||
mc("forge", "1.20.1")
|
||||
//WARNING: neoforge uses mods.toml instead of neoforge.mods.toml for versions 1.20.4 (?) and earlier
|
||||
mc("neoforge", "1.20.4", "1.21.1", "1.21.3", "1.21.4")
|
||||
//mc("neoforge", "1.20.4", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11")
|
||||
mc("neoforge", "1.20.4", "1.21.9", "1.21.11")
|
||||
}
|
||||
create(rootProject)
|
||||
}
|
||||
|
||||
rootProject.name = "TempLate"
|
||||
rootProject.name = "NeoComputers"
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.example.template;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class ConfigScreen extends Screen {
|
||||
|
||||
public ConfigScreen(Screen parent) {
|
||||
super(Text.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
context.drawCenteredTextWithShadow(client.textRenderer,
|
||||
"Hello, world",
|
||||
width / 2,
|
||||
height / 2,
|
||||
0xFFFFFFFF);
|
||||
}
|
||||
|
||||
public static ConfigScreen createConfigScreen(Screen parent) {
|
||||
return new ConfigScreen(parent);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.example.template;
|
||||
|
||||
public interface ModPlatform {
|
||||
String getModloader();
|
||||
boolean isModLoaded(String modloader);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.example.template;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TempLateInit
|
||||
{
|
||||
public static final String MODID = "template";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("Template");
|
||||
public static ModPlatform PLATFORM = null;
|
||||
|
||||
public static void entrypoint(ModPlatform platform) {
|
||||
TempLateInit.PLATFORM = platform;
|
||||
LOGGER.info("Started mod in %s loader".formatted(TempLateInit.PLATFORM.getModloader()));
|
||||
}
|
||||
}
|
||||
27
src/main/java/org/neoflock/neocomputers/ConfigScreen.java
Normal file
27
src/main/java/org/neoflock/neocomputers/ConfigScreen.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package org.neoflock.neocomputers;
|
||||
|
||||
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
public class ConfigScreen extends Screen {
|
||||
|
||||
public ConfigScreen(Screen parent) {
|
||||
super(Component.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) {
|
||||
super.render(guiGraphics, mouseX, mouseY, delta);
|
||||
guiGraphics.drawString(minecraft.font,
|
||||
"Hello, world",
|
||||
width / 2,
|
||||
height / 2,
|
||||
0xFFFFFFFF);
|
||||
}
|
||||
|
||||
public static ConfigScreen createConfigScreen(Screen parent) {
|
||||
return new ConfigScreen(parent);
|
||||
}
|
||||
}
|
||||
11
src/main/java/org/neoflock/neocomputers/ModPlatform.java
Normal file
11
src/main/java/org/neoflock/neocomputers/ModPlatform.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package org.neoflock.neocomputers;
|
||||
|
||||
|
||||
/**
|
||||
* This interface allows you to define platform specific code, and call it in
|
||||
*/
|
||||
|
||||
public interface ModPlatform {
|
||||
String getModloader();
|
||||
boolean isModLoaded(String modloader);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.neoflock.neocomputers;
|
||||
|
||||
import org.neoflock.neocomputers.platforms.fabric.NeoComputersFabric;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/*public class NeoComputersInit
|
||||
{
|
||||
public static final String MODID = "neocomputers";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("NeoComputers");
|
||||
public static ModPlatform PLATFORM = null;
|
||||
|
||||
public static void entrypoint(ModPlatform platform) {
|
||||
NeoComputersInit.PLATFORM = platform;
|
||||
LOGGER.info("Started mod in %s loader".formatted(NeoComputersInit.PLATFORM.getModloader()));
|
||||
LOGGER.info("Kotlin: %s".formatted(NeoComputers.INSTANCE.hello()));
|
||||
|
||||
}
|
||||
}*/
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.example.template.mixin;
|
||||
package org.neoflock.neocomputers.mixin;
|
||||
|
||||
import com.example.template.TempLateInit;
|
||||
import net.minecraft.client.gui.screen.TitleScreen;
|
||||
import org.neoflock.neocomputers.NeoComputers;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -12,7 +12,7 @@ public class ExampleMixin {
|
||||
|
||||
@Inject(method = "init",at=@At("HEAD"))
|
||||
void init(CallbackInfo ci){
|
||||
TempLateInit.LOGGER.info("Stonecutter example mixin init in %s".formatted(TempLateInit.PLATFORM.getModloader()));
|
||||
NeoComputers.INSTANCE.getLOGGER().info("Stonecutter example mixin init in %s".formatted(NeoComputers.INSTANCE.getPLATFORM().getModloader()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
//? if fabric {
|
||||
package com.example.template.platforms.fabric;
|
||||
package org.neoflock.neocomputers.platforms.fabric;
|
||||
|
||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||
import com.example.template.ConfigScreen;
|
||||
import org.neoflock.neocomputers.ConfigScreen;
|
||||
|
||||
public class ModMenuIntegration implements ModMenuApi {
|
||||
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||
@@ -1,15 +1,15 @@
|
||||
//? if fabric {
|
||||
package com.example.template.platforms.fabric;
|
||||
package org.neoflock.neocomputers.platforms.fabric;
|
||||
|
||||
import com.example.template.ModPlatform;
|
||||
import org.neoflock.neocomputers.ModPlatform;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import com.example.template.TempLateInit;
|
||||
import org.neoflock.neocomputers.NeoComputers;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public class TempLateFabric implements ModInitializer {
|
||||
public class NeoComputersFabric implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
TempLateInit.entrypoint(new FabricPlatform());
|
||||
NeoComputers.INSTANCE.entrypoint(new FabricPlatform());
|
||||
}
|
||||
public static class FabricPlatform implements ModPlatform{
|
||||
|
||||
@@ -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,17 +1,18 @@
|
||||
//? if forge {
|
||||
/*package com.example.template.platforms.forge;
|
||||
/*package org.neoflock.neocomputers.neocomputers.platforms.forge;
|
||||
|
||||
import com.example.template.ConfigScreen;
|
||||
import com.example.template.TempLateInit;
|
||||
import com.example.template.ModPlatform;
|
||||
import org.neoflock.neocomputers.ConfigScreen;
|
||||
import org.neoflock.neocomputers.NeoComputersInit;
|
||||
import org.neoflock.neocomputers.ModPlatform;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import org.neoflock.neocomputers.NeoComputers;
|
||||
|
||||
@Mod("template")
|
||||
public class TempLateForge {
|
||||
public TempLateForge() {
|
||||
TempLateInit.entrypoint(new ForgePlatform());
|
||||
@Mod("neocomputers")
|
||||
public class NeoComputersForge {
|
||||
public NeoComputersForge() {
|
||||
NeoComputers.INSTANCE.entrypoint(new ForgePlatform());
|
||||
MinecraftForge.registerConfigScreen(ConfigScreen::createConfigScreen);
|
||||
}
|
||||
public static class ForgePlatform implements ModPlatform {
|
||||
@@ -1,32 +1,31 @@
|
||||
//? if neoforge {
|
||||
/*package com.example.template.platforms.neoforge;
|
||||
/*package org.neoflock.neocomputers.platforms.neoforge;
|
||||
|
||||
import com.example.template.ConfigScreen;
|
||||
import com.example.template.TempLateInit;
|
||||
import com.example.template.ModPlatform;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import org.neoflock.neocomputers.ConfigScreen;
|
||||
import org.neoflock.neocomputers.ModPlatform;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.fml.ModLoadingContext;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import org.neoflock.neocomputers.NeoComputers;
|
||||
//? if <1.21 {
|
||||
import net.neoforged.neoforge.client.ConfigScreenHandler;
|
||||
//?} else {
|
||||
/^import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
|
||||
^///?}
|
||||
@Mod("template")
|
||||
public class TempLateNeoForge {
|
||||
public TempLateNeoForge() {
|
||||
TempLateInit.entrypoint(new NeoForgePlatform());
|
||||
/^import net.neoforged.neoforge.client.ConfigScreenHandler;
|
||||
^///?} else {
|
||||
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
|
||||
//?}
|
||||
@Mod("neocomputers")
|
||||
public class NeoComputersNeoForge {
|
||||
public NeoComputersNeoForge() {
|
||||
NeoComputers.INSTANCE.entrypoint(new NeoForgePlatform());
|
||||
ModLoadingContext.get().registerExtensionPoint(
|
||||
//? if <1.21 {
|
||||
ConfigScreenHandler.ConfigScreenFactory.class,
|
||||
/^ConfigScreenHandler.ConfigScreenFactory.class,
|
||||
() -> new ConfigScreenHandler.ConfigScreenFactory(
|
||||
((client, parent) -> ConfigScreen.createConfigScreen(parent))
|
||||
)
|
||||
//?} else {
|
||||
/^IConfigScreenFactory.class,
|
||||
^///?} else {
|
||||
IConfigScreenFactory.class,
|
||||
() -> (client, parent) -> ConfigScreen.createConfigScreen(parent)
|
||||
^///?}
|
||||
//?}
|
||||
);
|
||||
}
|
||||
public static class NeoForgePlatform implements ModPlatform {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.example.template.util;
|
||||
package org.neoflock.neocomputers.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
164
src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt
Normal file
164
src/main/kotlin/org/neoflock/neocomputers/NeoComputers.kt
Normal file
@@ -0,0 +1,164 @@
|
||||
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.LoggerFactory
|
||||
|
||||
object NeoComputers {
|
||||
const val MODID: String = "neocomputers"
|
||||
val LOGGER: Logger = LoggerFactory.getLogger("NeoComputers")
|
||||
var PLATFORM: ModPlatform? = null
|
||||
|
||||
// val BlockEntityRenderType: RenderType = RenderType.create(
|
||||
// "nc_blockentities",
|
||||
// DefaultVertexFormat.POSITION_TEX,
|
||||
// VertexFormat.Mode.QUADS,
|
||||
// 0xc000, RenderType.CompositeState.builder().setShaderState(RenderStateShard.POSITION_TEX_SHADER).createCompositeState(false)) // TODO: figure out correct buffer size and composite state
|
||||
|
||||
|
||||
fun entrypoint(platform: ModPlatform?) {
|
||||
PLATFORM = platform
|
||||
|
||||
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("Kotlin: %s".formatted(NeoComputers.hello()))
|
||||
LOGGER.info("Started mod in ${PLATFORM?.modloader} loader")
|
||||
LOGGER.info("Hello from kotlin!")
|
||||
}
|
||||
}
|
||||
19
src/main/kotlin/org/neoflock/neocomputers/block/BaseBlock.kt
Normal file
19
src/main/kotlin/org/neoflock/neocomputers/block/BaseBlock.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
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.state.BlockBehaviour
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import java.util.function.Supplier
|
||||
import com.google.common.base.Suppliers
|
||||
|
||||
open class BaseBlock(properties: Properties = Properties.of()) : Block(properties) { // TODO: create a TieredBaseBlock class that extends this or something
|
||||
// val tier: Int
|
||||
|
||||
companion object Registry {
|
||||
fun register(name: String, sup: Supplier<BaseBlock>): RegistrySupplier<Block> = Blocks.BLOCKS.register(name, sup);
|
||||
}
|
||||
}
|
||||
48
src/main/kotlin/org/neoflock/neocomputers/block/Blocks.kt
Normal file
48
src/main/kotlin/org/neoflock/neocomputers/block/Blocks.kt
Normal file
@@ -0,0 +1,48 @@
|
||||
package org.neoflock.neocomputers.block
|
||||
|
||||
import com.google.common.base.Suppliers
|
||||
import dev.architectury.registry.registries.DeferredRegister
|
||||
import dev.architectury.registry.registries.Registrar
|
||||
import dev.architectury.registry.registries.RegistrarManager
|
||||
import dev.architectury.registry.registries.RegistrySupplier
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour
|
||||
// import org.neoflock.neocomputers.item.Tabs
|
||||
// import org.neoflock.neocomputers.item.Items
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
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.Supplier
|
||||
|
||||
object Blocks {
|
||||
|
||||
|
||||
val BLOCKS: DeferredRegister<Block> = DeferredRegister.create(NeoComputers.MODID, Registries.BLOCK)
|
||||
val SCREEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("screen") { ScreenBlock() }
|
||||
val CAPACITOR_BLOCK: RegistrySupplier<Block> = BaseBlock.register("capacitor") { CapacitorBlock(1) }
|
||||
val CAPACITOR_BLOCK2: RegistrySupplier<Block> = BaseBlock.register("capacitor2") { CapacitorBlock(2) }
|
||||
val CAPACITOR_BLOCK3: RegistrySupplier<Block> = BaseBlock.register("capacitor3") { CapacitorBlock(3) }
|
||||
val SOLARGEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("solargen") { SolarGeneratorBlock() }
|
||||
val COMBUSTGEN_BLOCK: RegistrySupplier<Block> = BaseBlock.register("combustgen") { CombustionGeneratorBlock() }
|
||||
val CASE_BLOCK: RegistrySupplier<Block> = BaseBlock.register("case") { CaseBlock() }
|
||||
val REDSTONEIO_BLOCK: RegistrySupplier<Block> = BaseBlock.register("redio") { RedstoneIOBlock() }
|
||||
val CABLE_BLOCK: RegistrySupplier<Block> = BaseBlock.register("cable") { CableBlock() }
|
||||
val RELAY_BLOCK: RegistrySupplier<Block> = BaseBlock.register("relay") { RelayBlock() }
|
||||
val ROBOT_BLOCK: RegistrySupplier<Block> = BaseBlock.register("robot") { RobotBlock() }
|
||||
val RACK_BLOCK: RegistrySupplier<Block> = BaseBlock.register("rack") { RackBlock() }
|
||||
|
||||
fun registerBlockItems() {
|
||||
BLOCKS.forEach(Consumer { sup: RegistrySupplier<Block> ->
|
||||
NeoComputers.LOGGER.info(sup.id.toString())
|
||||
val id = ResourceKey.create(Registries.ITEM, sup.id)
|
||||
Items.ITEMS.register(sup.id.path) { 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); };
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user