Compare commits
151 Commits
2328169f2a
...
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 |
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:
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -453,3 +453,9 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.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!
|
||||
|
||||
@@ -8,8 +8,7 @@ the mod for that version!
|
||||
Also, try reading about how stonecutter's conditional macros work (those can be seen as the `//?` statements in the code).
|
||||
Stonecutter automatically comments and uncomments them when you switch between versions or loaders, you shouldn't do it yourself.
|
||||
|
||||
The minecraft version this mod is currently being developed on is 1.21.11 neoforge or fabric. Although the project stonecutter.gradle.kts
|
||||
is currently using 1.21.9-fabric, you can easily change it with the gradle task.
|
||||
The minecraft version this mod is currently being developed on is 1.21.11 neoforge or fabric.
|
||||
|
||||
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.
|
||||
35
TODO.md
Normal file
35
TODO.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Networking
|
||||
> All that is left is optimization
|
||||
|
||||
## Mergeable device nodes
|
||||
|
||||
For optimization of screen grouping and cables, we should add a system that allows merging nodes
|
||||
|
||||
## Copy the networking optimizations of OC
|
||||
> https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/src/main/scala/li/cil/oc/server/network/Network.scala
|
||||
|
||||
We should implement this, as it not only makes behavior match better, but also optimizes
|
||||
it a lot. We do need to replace the NEIGHBORS rule with implementing the SOME visibility.
|
||||
A direct port is not needed, just taking heavy inspiration from OC.
|
||||
|
||||
## Optimize power balancing
|
||||
|
||||
Use a smarter algorithm to prevent N storage nodes from iterating N^2 nodes each tick
|
||||
to balance power.
|
||||
|
||||
# Computation
|
||||
> Pretty important for a computer mod
|
||||
|
||||
## JNI
|
||||
|
||||
We need the JNI system so we can salvage our hard labor thrown into NeoNucleus.
|
||||
Also because it is a capable engine and has a good API for architectures, and NCL is very capable.
|
||||
|
||||
## Worker threads
|
||||
|
||||
Computers need worker threads for running non-synchronized code, because otherwise we're cooked
|
||||
|
||||
## Entities as machines
|
||||
|
||||
Aside from blocks like cases and robots, we should also support entities like drones.
|
||||
Not only for OC parity, but also as addons would def love that.
|
||||
@@ -11,6 +11,15 @@ plugins {
|
||||
val minecraft = stonecutter.current.version
|
||||
val 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 {
|
||||
@@ -21,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/")
|
||||
|
||||
@@ -32,12 +42,41 @@ repositories {
|
||||
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.officialMojangMappings())
|
||||
|
||||
modApi("dev.architectury:architectury:${mod.dep("architectury_version")}")
|
||||
mappings(loom.layered(){
|
||||
var date = ""
|
||||
when (minecraft) {
|
||||
"1.20.1" -> date = "2023.09.03"
|
||||
"1.20.4" -> date = "2024.04.14"
|
||||
"1.21.1" -> date = "2024.11.17"
|
||||
"1.21.9" -> date = "2025.10.05"
|
||||
"1.21.11" -> date = "2025.12.20"
|
||||
else -> date="idk lol 67"
|
||||
}
|
||||
|
||||
officialMojangMappings() // TODO: versions
|
||||
parchment("org.parchmentmc.data:parchment-${minecraft}:${date}@zip")
|
||||
})
|
||||
var archversion = "idk"
|
||||
when(minecraft) { // NOTE: add more entries if you want to add more versions
|
||||
"1.20.1" -> archversion = "9.2.14"
|
||||
"1.20.4" -> archversion = "11.1.17"
|
||||
"1.21.1" -> archversion = "13.0.8"
|
||||
"1.21.9" -> archversion = "18.0.5"
|
||||
else -> archversion = "19.0.1"
|
||||
}
|
||||
|
||||
// modImplementation("dev.architectury:architectury:$rootProject.architectury_api_version")
|
||||
// modImplementation("dev.architectury:architectury:${archversion}")
|
||||
modImplementation("dev.architectury:architectury:${archversion}")
|
||||
if (loader == "fabric") {
|
||||
modImplementation("net.fabricmc:fabric-loader:${mod.dep("fabric_loader")}")
|
||||
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||
@@ -45,16 +84,26 @@ dependencies {
|
||||
//some features (like automatic resource loading from non vanilla namespaces) work only with fabric API installed
|
||||
//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:${mod.dep("architectury_version")}")
|
||||
modApi("dev.architectury:architectury-fabric:${archversion}")
|
||||
fun getTeamRebornEnergy(): String {
|
||||
val curVer = minecraftVersionToNum(minecraft)
|
||||
if (curVer < minecraftVersionToNum("1.20.5")) { return "3.0.0" }
|
||||
if (curVer < minecraftVersionToNum("1.21.0")) { return "4.0.0" }
|
||||
if (curVer < minecraftVersionToNum("1.21.5")) { return "4.1.0" }
|
||||
if (curVer < minecraftVersionToNum("26.1")) { return "4.2.0" }
|
||||
return "5.0.0"
|
||||
}
|
||||
modApi("teamreborn:energy:${getTeamRebornEnergy()}")
|
||||
}
|
||||
if (loader == "forge") {
|
||||
"forge"("net.minecraftforge:forge:${minecraft}-${mod.dep("forge_loader")}")
|
||||
//implementation("thedarkcolour:kotlinforforge:1.16.0")
|
||||
// mappings("net.fabricmc:yarn:$minecraft+build.${mod.dep("yarn_build")}:v2")
|
||||
|
||||
modApi("dev.architectury:architectury-forge:${mod.dep("architectury_version")}")
|
||||
modApi("dev.architectury:architectury-forge:${archversion}")
|
||||
"io.github.llamalad7:mixinextras-forge:${mod.dep("mixin_extras")}".let {
|
||||
implementation(it)
|
||||
include(it)
|
||||
@@ -69,8 +118,10 @@ dependencies {
|
||||
// }
|
||||
// })
|
||||
|
||||
modApi("dev.architectury:architectury-forge:${mod.dep("architectury_version")}")
|
||||
if (minecraft=="1.21.9" || minecraft=="1.21.11") modApi("dev.architectury:architectury-neoforge:${archversion}")
|
||||
else modApi("dev.architectury:architectury-forge:${archversion}") // NOTE: this could be wrong
|
||||
implementation("thedarkcolour:kotlinforforge-neoforge:6.0.0")
|
||||
|
||||
}
|
||||
}
|
||||
buildscript {
|
||||
@@ -225,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ mod.mc_targets=[VERSIONED]
|
||||
deps.mixin_extras=0.4.1
|
||||
deps.fabric_loader=0.18.3
|
||||
deps.fabric_version=[VERSIONED]
|
||||
deps.architectury_version=9.1.12
|
||||
|
||||
deps.forge_loader=[VERSIONED]
|
||||
deps.neoforge_loader=[VERSIONED]
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
310
gradlew
vendored
Normal file → Executable file
310
gradlew
vendored
Normal file → Executable file
@@ -1,78 +1,128 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
# This is normally unused
|
||||
# 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 -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"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -81,96 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
if $JAVACMD --add-opens java.base/java.lang=ALL-UNNAMED -version ; then
|
||||
DEFAULT_JVM_OPTS="--add-opens java.base/java.lang=ALL-UNNAMED $DEFAULT_JVM_OPTS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
79
gradlew.bat
vendored
79
gradlew.bat
vendored
@@ -1,4 +1,22 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -9,25 +27,29 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -35,48 +57,35 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@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 %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -23,7 +23,7 @@ stonecutter {
|
||||
}
|
||||
//i would recommend to use neoforge for mc > 1.20.1, i haven't tested neocomputers for forge on versions higher than that
|
||||
//mc("fabric","1.20.1","1.20.4", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11")
|
||||
mc("fabric", "1.20.1", "1.20.4", "1.21.9", "1.21.11")
|
||||
mc("fabric", "1.20.1", "1.20.4", "1.21.1", "1.21.9", "1.21.11")
|
||||
mc("forge", "1.20.1")
|
||||
//WARNING: neoforge uses mods.toml instead of neoforge.mods.toml for versions 1.20.4 (?) and earlier
|
||||
//mc("neoforge", "1.20.4", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11")
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.neoflock.neocomputers.platforms.fabric.client;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
||||
import net.minecraft.client.color.item.ItemColor;
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import org.neoflock.neocomputers.NeoComputers;
|
||||
import org.neoflock.neocomputers.block.Blocks;
|
||||
import org.neoflock.neocomputers.block.CableBlock;
|
||||
import org.neoflock.neocomputers.entity.BlockEntities;
|
||||
import org.neoflock.neocomputers.entity.render.*;
|
||||
import org.neoflock.neocomputers.item.Items;
|
||||
import org.neoflock.neocomputers.platforms.fabric.client.model.ModelLoader;
|
||||
|
||||
public class NeoComputersFabricClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ModelLoadingPlugin.register(new ModelLoader());
|
||||
BlockEntityRenderers.register(BlockEntities.INSTANCE.getSCREEN_ENTITY().get(), ScreenEntityRenderer::new); // TODO: put this in common
|
||||
BlockEntityRenderers.register(BlockEntities.INSTANCE.getCASE_ENTITY().get(), CaseEntityRenderer::new);
|
||||
BlockEntityRenderers.register(BlockEntities.INSTANCE.getRELAY_ENTITY().get(), RelayEntityRenderer::new);
|
||||
BlockEntityRenderers.register(BlockEntities.INSTANCE.getROBOT_ENTITY().get(), RobotEntityRenderer::new);
|
||||
BlockEntityRenderers.register(BlockEntities.INSTANCE.getRACK_ENTITY().get(), RackEntityRenderer::new);
|
||||
|
||||
ColorProviderRegistry.BLOCK.register((state, world, pos, index) -> {
|
||||
if (index == 0) {
|
||||
return state.getValue(CableBlock.Companion.getCOLOR()).getTextureDiffuseColor();
|
||||
} else {
|
||||
return 0xFFFFFF;
|
||||
}
|
||||
}, Blocks.INSTANCE.getCABLE_BLOCK().get());
|
||||
|
||||
ColorProviderRegistry.ITEM.register((stack, index)-> {
|
||||
return DyeColor.LIGHT_GRAY.getTextureDiffuseColor();
|
||||
}, Items.INSTANCE.getITEMS().getRegistrar().get(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "cable")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.neoflock.neocomputers.platforms.fabric.client.model;
|
||||
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.neoflock.neocomputers.NeoComputers;
|
||||
import org.neoflock.neocomputers.block.model.AbstractModel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class FabricModelWrapper implements FabricBakedModel, UnbakedModel {
|
||||
private AbstractModel model;
|
||||
|
||||
public FabricModelWrapper(AbstractModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<RandomSource> randomSupplier, RenderContext context) {
|
||||
VanillaModelEncoder.emitBlockQuads(model, state, randomSupplier, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitItemQuads(ItemStack stack, Supplier<RandomSource> randomSupplier, RenderContext context) {
|
||||
VanillaModelEncoder.emitItemQuads(model, null, randomSupplier, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getDependencies() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveParents(Function<ResourceLocation, UnbakedModel> resolver) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BakedModel bake(ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state) {
|
||||
// NeoComputers.INSTANCE.getLOGGER().info("{}", spriteGetter.apply(new Material()));
|
||||
model._bake(spriteGetter);
|
||||
// model.bake((Function1<? super ResourceLocation, ? extends TextureAtlasSprite>) Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS));
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.neoflock.neocomputers.platforms.fabric.client.model;
|
||||
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.neoflock.neocomputers.NeoComputers;
|
||||
import org.neoflock.neocomputers.block.model.RobotModel;
|
||||
|
||||
public class ModelLoader implements ModelLoadingPlugin {
|
||||
public static final ResourceLocation ROBOT = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "robot");
|
||||
|
||||
@Override
|
||||
public void onInitializeModelLoader(Context pluginContext) {
|
||||
pluginContext.modifyModelOnLoad().register((original, context) -> {
|
||||
final ModelResourceLocation id = context.topLevelId();
|
||||
if (id != null && id.id().equals(ROBOT)) {
|
||||
return new FabricModelWrapper(new RobotModel());
|
||||
}
|
||||
return original;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
//? if neoforge {
|
||||
/*package org.neoflock.neocomputers.neocomputers.platforms.neoforge;
|
||||
/*package org.neoflock.neocomputers.platforms.neoforge;
|
||||
|
||||
import org.neoflock.neocomputers.ConfigScreen;
|
||||
import org.neoflock.neocomputers.NeoComputersInit;
|
||||
import org.neoflock.neocomputers.ModPlatform;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.fml.ModLoadingContext;
|
||||
|
||||
@@ -1,19 +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 ${NeoComputers.PLATFORM?.modloader} loader")
|
||||
LOGGER.info("Started mod in ${PLATFORM?.modloader} loader")
|
||||
LOGGER.info("Hello from kotlin!")
|
||||
}
|
||||
}
|
||||
@@ -1,14 +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
|
||||
|
||||
class BaseBlock : Block {
|
||||
protected val tier: Int
|
||||
constructor(tier: Int): super(Properties.of()) {
|
||||
this.tier = tier
|
||||
}
|
||||
open class BaseBlock(properties: Properties = Properties.of()) : Block(properties) { // TODO: create a TieredBaseBlock class that extends this or something
|
||||
// val tier: Int
|
||||
|
||||
public fun getTier(): Int {
|
||||
return tier
|
||||
companion object Registry {
|
||||
fun register(name: String, sup: Supplier<BaseBlock>): RegistrySupplier<Block> = Blocks.BLOCKS.register(name, sup);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,48 @@
|
||||
package org.libreflock.neocomputers.block
|
||||
package org.neoflock.neocomputers.block
|
||||
|
||||
import com.google.common.base.Suppliers
|
||||
import dev.architectury.registry.registries.DeferredRegister
|
||||
import dev.architectury.registry.registries.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 org.libreflock.neocomputers.item.Items
|
||||
import org.libreflock.neocomputers.item.Tabs
|
||||
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 CASE: MutableList<RegistrySupplier<Block?>?>? =
|
||||
BaseBlock.register(intArrayOf(0, 1, 2), "case", { tier -> CaseBlock(tier) })
|
||||
|
||||
// public static final RegistrySupplier<Block> CASE0 = BLOCKS.register("case0", () -> new CaseBlock(0));
|
||||
// public static final RegistrySupplier<Block> CASE1 = BLOCKS.register("case1", () -> new CaseBlock(1));
|
||||
// public static final RegistrySupplier<Block> CASE2 = BLOCKS.register("case2", () -> new CaseBlock(2));
|
||||
// public static final RegistrySupplier<Block> CABLE = BLOCKS.register("cable", () -> new CableBlock());
|
||||
val SCREEN: RegistrySupplier<Block?>? = BLOCKS.register<Block?>("screen", Supplier { ScreenBlock() })
|
||||
val CABLE: RegistrySupplier<Block?>? = BLOCKS.register<Block?>("cable", Supplier { CableBlock() })
|
||||
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?>? ->
|
||||
// sup.pre
|
||||
// sup.((blk) -> {
|
||||
// NeoComputers.LOGGER.info(blk.getDescriptionId());
|
||||
// if (blk instanceof BaseBlock) {
|
||||
// Items.ITEMS.register(sup.getId().getPath(), () -> new BaseBlock.BaseBlockItem(blk, new Item.Properties().arch$tab(Tabs.TAB)));
|
||||
// } else {
|
||||
// Items.ITEMS.register(sup.getId().getPath(), () -> new BlockItem(blk, new Item.Properties().arch$tab(Tabs.TAB)));
|
||||
// }
|
||||
// });
|
||||
Items.ITEMS.register(sup!!.getId().getPath(), {
|
||||
if (sup.get() is BaseBlock) {
|
||||
return@register BaseBlockItem(sup.get(), Item.Properties().`arch$tab`(Tabs.TAB))
|
||||
} else {
|
||||
return@register BlockItem(sup.get(), Item.Properties().`arch$tab`(Tabs.TAB))
|
||||
}
|
||||
})
|
||||
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); };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package org.neoflock.neocomputers.network
|
||||
|
||||
import dev.architectury.networking.NetworkManager
|
||||
import io.netty.buffer.Unpooled
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.level.Level
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import java.time.Duration
|
||||
|
||||
object NodeSynchronizer {
|
||||
val MAX_STATE_DISTANCE_ALLOWED = 128
|
||||
|
||||
class DeviceBlockStatePayload(var blockPos: BlockPos, var buffers: List<FriendlyByteBuf>): CustomPacketPayload {
|
||||
companion object {
|
||||
val BLOCKDEV_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "blockdev_sync")
|
||||
val TYPE = CustomPacketPayload.Type<DeviceBlockStatePayload>(BLOCKDEV_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStatePayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStatePayload {
|
||||
val blockPos = buf.readBlockPos()
|
||||
val bufferCount = buf.readVarInt()
|
||||
val buffers = List(bufferCount) {
|
||||
val bytes = buf.readByteArray()
|
||||
val rawBuf = Unpooled.buffer(bytes.size)
|
||||
rawBuf.writeBytes(bytes)
|
||||
FriendlyByteBuf(rawBuf)
|
||||
}
|
||||
return DeviceBlockStatePayload(blockPos, buffers)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: DeviceBlockStatePayload) {
|
||||
buf.writeBlockPos(payload.blockPos)
|
||||
buf.writeVarInt(payload.buffers.size)
|
||||
payload.buffers.forEach {
|
||||
buf.writeByteArray(it.array())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class DeviceBlockStateRequest(var blockPos: BlockPos): CustomPacketPayload {
|
||||
companion object {
|
||||
val BLOCKDEV_REQ_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "blockdev_statereq")
|
||||
val TYPE = CustomPacketPayload.Type<DeviceBlockStateRequest>(BLOCKDEV_REQ_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStateRequest> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStateRequest {
|
||||
val blockPos = buf.readBlockPos()
|
||||
return DeviceBlockStateRequest(blockPos)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: DeviceBlockStateRequest) {
|
||||
buf.writeBlockPos(payload.blockPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class ScreenPayload(var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenPayload>(SCREEN_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): ScreenPayload {
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return ScreenPayload(buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenPayload) {
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class ScreenDataPayload(var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_DATA_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload {
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return ScreenDataPayload(buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenDataPayload) {
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class BeepDataPayload(val pos: BlockPos, val pattern: String, val freq: Int, val duration: Duration, val volume: Double): CustomPacketPayload {
|
||||
companion object {
|
||||
val BEEP_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "beep_data")
|
||||
val TYPE = CustomPacketPayload.Type<BeepDataPayload>(BEEP_DATA_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, BeepDataPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): BeepDataPayload {
|
||||
val pos = buf.readBlockPos()
|
||||
val pattern = buf.readUtf()
|
||||
val freq = buf.readVarInt()
|
||||
val duration = buf.readVarLong()
|
||||
val volume = buf.readDouble()
|
||||
return BeepDataPayload(pos, pattern, freq, Duration.ofMillis(duration), volume)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: BeepDataPayload) {
|
||||
buf.writeBlockPos(payload.pos)
|
||||
buf.writeUtf(payload.pattern)
|
||||
buf.writeVarInt(payload.freq)
|
||||
buf.writeVarLong(payload.duration.toMillis())
|
||||
buf.writeDouble(payload.volume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
val screenMap = HashMap<ServerPlayer, DeviceNode>()
|
||||
|
||||
fun playerScreenClosed(player: ServerPlayer) {
|
||||
screenMap.remove(player)
|
||||
}
|
||||
|
||||
fun registerPlayerScreen(player: ServerPlayer, devNode: DeviceNode) {
|
||||
screenMap[player] = devNode
|
||||
}
|
||||
|
||||
fun nodeErased(node: DeviceNode) {
|
||||
var player: ServerPlayer? = null
|
||||
for((p, n) in screenMap) {
|
||||
if(n == node) player = p
|
||||
}
|
||||
if(player != null) screenMap.remove(player)
|
||||
}
|
||||
|
||||
fun syncScreens() {
|
||||
for((player, ent) in screenMap) {
|
||||
val buf = FriendlyByteBuf(Unpooled.buffer())
|
||||
ent.encodeScreenData(player, buf)
|
||||
NetworkManager.sendToPlayer(player, ScreenPayload(buf))
|
||||
}
|
||||
}
|
||||
|
||||
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
|
||||
NetworkManager.sendToServer(ScreenDataPayload(friendlyByteBuf))
|
||||
}
|
||||
|
||||
fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) {
|
||||
if(level is ServerLevel) {
|
||||
level.players().forEach {
|
||||
NetworkManager.sendToPlayer(it, beepDataPayload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.neoflock.neocomputers.network
|
||||
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import org.neoflock.neocomputers.block.DeviceBlockEntity
|
||||
//? if fabric {
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext
|
||||
import net.minecraft.core.Direction
|
||||
import team.reborn.energy.api.EnergyStorage;
|
||||
//?}
|
||||
|
||||
// our soul purpose is to fuse bullshit power APIs together
|
||||
// the NodeBlockEntity and Node given us a way to get power from a block, we just
|
||||
// need to tell mods how to do it as well
|
||||
object PowerManager {
|
||||
fun<T: DeviceBlockEntity> registerPowerDevice(blockEntityType: BlockEntityType<T>) {
|
||||
//? if fabric {
|
||||
EnergyStorage.SIDED.registerForBlockEntity({
|
||||
// TODO: as this is currently written, if the node instance changes and the mod cached the conversion, we're boned. Consider fixing it.
|
||||
entity, dir ->
|
||||
val node = entity.getNodeFromSide(dir ?: Direction.UP)
|
||||
if(node == null) null else object : EnergyStorage {
|
||||
override fun getAmount() = node.energy
|
||||
override fun getCapacity() = node.energyCapacity
|
||||
override fun supportsExtraction() = node.powerRole != PowerRole.CONSUMER && node.energyCapacity > 0
|
||||
override fun supportsInsertion() = node.powerRole != PowerRole.GENERATOR
|
||||
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(node.powerRole == PowerRole.CONSUMER) return 0
|
||||
val taken = node.withdrawEnergy(maxAmount)
|
||||
transaction?.addCloseCallback {
|
||||
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) node.giveEnergy(taken)
|
||||
}
|
||||
return taken
|
||||
}
|
||||
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(node.powerRole == PowerRole.GENERATOR) return 0
|
||||
val given = node.giveEnergy(maxAmount)
|
||||
transaction?.addCloseCallback { ctx, res ->
|
||||
if (res.wasAborted() || !res.wasCommitted()) node.withdrawEnergy(given)
|
||||
}
|
||||
return given
|
||||
}
|
||||
}
|
||||
}, blockEntityType);
|
||||
//?}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.neoflock.neocomputers.sounds
|
||||
|
||||
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance
|
||||
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
|
||||
import net.minecraft.client.resources.sounds.MinecartSoundInstance
|
||||
import net.minecraft.client.resources.sounds.SoundInstance
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import org.neoflock.neocomputers.entity.MachineEntity
|
||||
|
||||
class ComputerRunningSoundInstance: AbstractTickableSoundInstance {
|
||||
val machine: MachineEntity
|
||||
|
||||
fun updatePosition() {
|
||||
val pos = machine.getMachineBlockPosition()
|
||||
this.x = pos.x.toDouble() + 0.5
|
||||
this.y = pos.y.toDouble() + 0.5
|
||||
this.z = pos.z.toDouble() + 0.5
|
||||
}
|
||||
|
||||
constructor(machine: MachineEntity, soundEvent: SoundEvent, soundSource: SoundSource): super(soundEvent, soundSource, SoundInstance.createUnseededRandom()) {
|
||||
this.machine = machine
|
||||
this.looping = true
|
||||
this.delay = 0
|
||||
this.volume = 1.0F
|
||||
this.pitch = 1.0F
|
||||
updatePosition()
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
if(!machine.isRunning()) {
|
||||
this.stop()
|
||||
} else {
|
||||
updatePosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
163
src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt
Normal file
163
src/main/kotlin/org/neoflock/neocomputers/sounds/Sounds.kt
Normal file
@@ -0,0 +1,163 @@
|
||||
package org.neoflock.neocomputers.sounds
|
||||
|
||||
import dev.architectury.registry.registries.DeferredRegister
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.lwjgl.BufferUtils
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.lwjgl.openal.AL10
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.experimental.xor
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sin
|
||||
|
||||
object Sounds {
|
||||
val SOUNDS = DeferredRegister.create(NeoComputers.MODID, Registries.SOUND_EVENT)!!
|
||||
|
||||
val COMPUTER_RUNNING = registerSound("computer_running")
|
||||
|
||||
fun registerSound(name: String) = SOUNDS.register(name) {
|
||||
SoundEvent.createVariableRangeEvent(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, name))
|
||||
}!!
|
||||
|
||||
val BEEP_SAMPLERATE = 44100
|
||||
val BEEP_AMPLITUDE = 32f
|
||||
val BEEP_MAXDIST = 16f
|
||||
|
||||
// Also largely taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala
|
||||
|
||||
val allSounds = ThreadLocal.withInitial { mutableListOf<CustomSoundBuffer>() }
|
||||
|
||||
class CustomSoundBuffer {
|
||||
var dead: Boolean = true
|
||||
var buffer: Int = -1
|
||||
var source: Int = -1
|
||||
|
||||
fun start(x: Float, y: Float, z: Float, data: ByteBuffer, gain: Float): Int? {
|
||||
// clear errors or smth idk
|
||||
AL10.alGetError()
|
||||
|
||||
// written in a C style by a C dev
|
||||
// all this work on a JVM project and I'm still writing C
|
||||
// would be better if Kotlin had goto btw just saying
|
||||
val ok = AL10.AL_NO_ERROR
|
||||
var err = ok
|
||||
buffer = AL10.alGenBuffers()
|
||||
err = AL10.alGetError()
|
||||
if(err != ok) return err
|
||||
|
||||
AL10.alBufferData(buffer, AL10.AL_FORMAT_MONO8, data, BEEP_SAMPLERATE)
|
||||
err = AL10.alGetError()
|
||||
if(err != ok) {
|
||||
AL10.alDeleteBuffers(buffer)
|
||||
return err
|
||||
}
|
||||
|
||||
source = AL10.alGenSources()
|
||||
err = AL10.alGetError()
|
||||
if(err != ok) {
|
||||
AL10.alDeleteBuffers(buffer)
|
||||
return err
|
||||
}
|
||||
|
||||
AL10.alSourceQueueBuffers(source, buffer)
|
||||
err = AL10.alGetError()
|
||||
if(err != ok) {
|
||||
AL10.alDeleteBuffers(buffer)
|
||||
AL10.alDeleteSources(source)
|
||||
return err
|
||||
}
|
||||
|
||||
AL10.alSource3f(source, AL10.AL_POSITION, x, y, z)
|
||||
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, BEEP_MAXDIST)
|
||||
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, BEEP_MAXDIST)
|
||||
AL10.alSourcef(source, AL10.AL_GAIN, gain * 0.3f)
|
||||
err = AL10.alGetError()
|
||||
if(err != ok) {
|
||||
AL10.alDeleteBuffers(buffer)
|
||||
AL10.alDeleteSources(source)
|
||||
return err
|
||||
}
|
||||
|
||||
AL10.alSourcePlay(source)
|
||||
err = AL10.alGetError()
|
||||
if(err != ok) {
|
||||
AL10.alDeleteBuffers(buffer)
|
||||
AL10.alDeleteSources(source)
|
||||
return err
|
||||
}
|
||||
|
||||
dead = false
|
||||
return null
|
||||
}
|
||||
|
||||
fun checkDone(): Boolean {
|
||||
if(dead) return true
|
||||
if(AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) return false
|
||||
dead = true
|
||||
AL10.alDeleteSources(source)
|
||||
AL10.alDeleteBuffers(buffer)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
fun beep(pos: Vec3, pattern: String, frequency: Int = 1000, duration: Int = 200) {
|
||||
NeoComputers.LOGGER.info("Beep: $pattern, $frequency Hz, $duration ms")
|
||||
val mc = Minecraft.getInstance()
|
||||
val playerPos = mc.player?.position() ?: pos
|
||||
val distanceBasedGain = max(0.0, 1 - pos.distanceTo(playerPos) / BEEP_MAXDIST).toFloat()
|
||||
val volume = 1.0
|
||||
val gain = distanceBasedGain * volume
|
||||
if (gain <= 0 || BEEP_AMPLITUDE <= 0) return
|
||||
|
||||
// Algorithm effectively ported over from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/util/Audio.scala
|
||||
// We do add support for spaces tho
|
||||
val charArr = pattern.toCharArray()
|
||||
val sampleCounts = charArr.map { if(it == '.') duration else 2 * duration }.map { it * BEEP_SAMPLERATE / 1000 }
|
||||
val pauseSample = 50 * BEEP_SAMPLERATE / 1000
|
||||
|
||||
val finalBuf = BufferUtils.createByteBuffer(sampleCounts.sum() + pauseSample * sampleCounts.lastIndex)
|
||||
val step = frequency.toFloat() / BEEP_SAMPLERATE
|
||||
var off = 0f
|
||||
for((i, sampleCount) in sampleCounts.withIndex()) {
|
||||
if(charArr[i] == ' ') {
|
||||
for(sample in 0..<sampleCount) {
|
||||
finalBuf.put(127)
|
||||
}
|
||||
} else {
|
||||
for(sample in 0..<sampleCount) {
|
||||
val angle = 2 * PI * off
|
||||
val value = (sin(angle).sign * BEEP_AMPLITUDE).toInt().toByte().xor(0x80.toByte())
|
||||
off += step
|
||||
if(off > 1) off -= 1f
|
||||
finalBuf.put(value)
|
||||
}
|
||||
}
|
||||
if(finalBuf.hasRemaining()) {
|
||||
for(sample in 0..<pauseSample) {
|
||||
finalBuf.put(127)
|
||||
}
|
||||
}
|
||||
}
|
||||
finalBuf.rewind()
|
||||
|
||||
val sound = CustomSoundBuffer()
|
||||
val soundErr = sound.start(pos.x.toFloat(), pos.y.toFloat(), pos.z.toFloat(), finalBuf, gain.toFloat())
|
||||
if(soundErr != null) {
|
||||
NeoComputers.LOGGER.error("Playing beep failed, OpenAL exit code of $soundErr")
|
||||
return
|
||||
}
|
||||
|
||||
NeoComputers.LOGGER.info("Beeping with ${finalBuf.capacity()} samples")
|
||||
allSounds.get().addLast(sound)
|
||||
}
|
||||
|
||||
fun tickCustomSounds() {
|
||||
allSounds.get().removeIf { it.checkDone() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.neoflock.neocomputers.utils
|
||||
|
||||
import dev.architectury.registry.fuel.FuelRegistry
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
// mewhenthe, aka e, will have me publicly executed for this code
|
||||
object ContainerUtils {
|
||||
fun getBurningTime(itemStack: ItemStack): Int? {
|
||||
val time = FuelRegistry.get(itemStack)
|
||||
if(time == 0) return null
|
||||
return time
|
||||
}
|
||||
|
||||
fun isBurningFuel(itemStack: ItemStack) = getBurningTime(itemStack) != null
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.neoflock.neocomputers.utils;
|
||||
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.Resource
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
/*
|
||||
* OC hex font format:
|
||||
* 5 character hex code .. ":" .. variable length hex code .. LF
|
||||
* this is essentially a dictionary
|
||||
* */
|
||||
object FontProvider {
|
||||
val map: MutableMap<Char, ArrayList<Byte>> = mutableMapOf();
|
||||
|
||||
fun load(loc: ResourceLocation) {
|
||||
var man: ResourceManager = Minecraft.getInstance().resourceManager
|
||||
var resource: Resource = man.getResourceOrThrow(loc)
|
||||
var stream = resource.open()
|
||||
var bfr = stream.bufferedReader();
|
||||
while (stream.available() > 0) {
|
||||
var line: String = bfr.readLine()
|
||||
var splitLine = line.split(":");
|
||||
var key = splitLine[0].hexToInt().toChar();
|
||||
var value: ByteArray = splitLine[1].hexToByteArray(); // shout out to the kotlin stdlib for having ts
|
||||
var bytes: ArrayList<Byte> = value.toCollection(ArrayList<Byte>());
|
||||
map[key] = bytes
|
||||
}
|
||||
NeoComputers.LOGGER.info("[FontProvider] Loaded font!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.neoflock.neocomputers.utils
|
||||
|
||||
object Formatting {
|
||||
fun formatMemory(size: Long, spacing: String = " "): String {
|
||||
var unit = 0
|
||||
val units = listOf("B", "KiB", "MiB", "GiB", "TiB", "PiB")
|
||||
var num = size.toDouble()
|
||||
while(unit < units.lastIndex && num >= 1024) {
|
||||
num /= 1024
|
||||
unit++
|
||||
}
|
||||
num = (num * 100).toInt().toDouble() / 100
|
||||
return "$num$spacing${units[unit]}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package org.neoflock.neocomputers.utils
|
||||
|
||||
// based off the ImplementedContainer of https://docs.fabricmc.net/develop/blocks/block-containers
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import com.mojang.blaze3d.vertex.VertexFormat
|
||||
import dev.architectury.registry.menu.MenuRegistry
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.AbstractWidget
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.client.renderer.RenderStateShard.ShaderStateShard
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.Resource
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.gui.widget.DynamicSlot
|
||||
|
||||
// Common container interface, assumes the entire purpose is purely raw item storage
|
||||
interface GenericContainer : Container {
|
||||
fun getItems(): NonNullList<ItemStack>
|
||||
|
||||
override fun getContainerSize(): Int {
|
||||
return getItems().size
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return getItems().all { it.isEmpty }
|
||||
}
|
||||
|
||||
override fun getItem(i: Int): ItemStack {
|
||||
return getItems()[i]
|
||||
}
|
||||
|
||||
override fun removeItem(slot: Int, count: Int): ItemStack {
|
||||
val res = ContainerHelper.removeItem(getItems(), slot, count)
|
||||
if (!res.isEmpty) setChanged()
|
||||
return res
|
||||
}
|
||||
|
||||
override fun setItem(slot: Int, itemStack: ItemStack) {
|
||||
getItems()[slot] = itemStack
|
||||
|
||||
// in case of bullshit
|
||||
if(itemStack.count > itemStack.maxStackSize) {
|
||||
// rip items
|
||||
itemStack.count = itemStack.maxStackSize
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeItemNoUpdate(i: Int): ItemStack {
|
||||
return ContainerHelper.takeItem(getItems(), i)
|
||||
}
|
||||
|
||||
override fun clearContent() {
|
||||
getItems().clear()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var container: Container): AbstractContainerMenu(menuType, id) {
|
||||
fun addInventorySlots(inventory: Inventory, x: Int, y: Int) {
|
||||
// Based off the code in ChestMenu
|
||||
for (i in 0..2) {
|
||||
for (j in 0..8) {
|
||||
this.addSlot(Slot(inventory, j + i * 9 + 9, 8 + j * 18, y + i * 18))
|
||||
}
|
||||
}
|
||||
|
||||
addInventoryHotbar(inventory, x, y + 3 * 18 + 4)
|
||||
}
|
||||
|
||||
fun addInventoryHotbar(inventory: Inventory, x: Int, y: Int) {
|
||||
for (i in 0..8) {
|
||||
this.addSlot(Slot(inventory, i, x + i * 18, y))
|
||||
}
|
||||
}
|
||||
|
||||
// taken from https://docs.fabricmc.net/develop/blocks/container-menus
|
||||
override fun quickMoveStack(player: Player, i: Int): ItemStack? {
|
||||
val slot = slots[i]
|
||||
|
||||
if(!slot.hasItem()) return ItemStack.EMPTY
|
||||
|
||||
val stack = slot.item
|
||||
val copied = stack.copy()
|
||||
val contSize = container.containerSize
|
||||
|
||||
if(i < contSize) {
|
||||
if(!this.moveItemStackTo(stack, contSize, slots.size, true)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else if(!this.moveItemStackTo(stack, 0, contSize, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
if(stack.isEmpty) {
|
||||
slot.setByPlayer(ItemStack.EMPTY)
|
||||
} else {
|
||||
slot.setChanged()
|
||||
}
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean {
|
||||
return container.stillValid(player)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenericContainerScreen<T: GenericContainerMenu>(menu: T, inventory: Inventory, component: Component): AbstractContainerScreen<T>(menu, inventory, component) {
|
||||
open fun shouldCenterTitle() = true
|
||||
open fun shouldRenderTooltip() = true
|
||||
open fun findMenuTexture(): ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/background.png")
|
||||
|
||||
open fun getBoundBlockEntityType(): Set<BlockEntityType<*>> = setOf()
|
||||
|
||||
open fun processScreenStatePacket(buf: FriendlyByteBuf) {}
|
||||
|
||||
val imageX: Int
|
||||
get() = (width - imageWidth) / 2
|
||||
|
||||
val imageY: Int
|
||||
get() = (height - imageHeight) / 2
|
||||
|
||||
var widgets = mutableListOf<AbstractWidget>()
|
||||
|
||||
val RENDER_TYPE = { r: ResourceLocation ->
|
||||
RenderType.create("nc_gui_bg", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.QUADS, RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder()
|
||||
.setShaderState(ShaderStateShard.POSITION_TEX_SHADER)
|
||||
.setTextureState(RenderStateShard.TextureStateShard(r, false, false))
|
||||
.createCompositeState(false))
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
if(shouldCenterTitle()) this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2
|
||||
}
|
||||
|
||||
override fun renderBg(guiGraphics: GuiGraphics, f: Float, i: Int, j: Int) {
|
||||
val menuTex = findMenuTexture()
|
||||
val cx = (width - imageWidth) / 2
|
||||
val cy = (height - imageHeight) / 2
|
||||
guiGraphics.pose().pushPose()
|
||||
guiGraphics.pose().translate(cx.toFloat(), cy.toFloat(), 0f)
|
||||
|
||||
guiGraphics.blit(menuTex, 0, 0, 0, 0, imageWidth, imageHeight)
|
||||
renderbg(guiGraphics, f, i-cx, j-cy)
|
||||
|
||||
for (widget in widgets) {
|
||||
widget.render(guiGraphics, i-cx, j-cy, f)
|
||||
}
|
||||
|
||||
for (slot in menu.slots) {
|
||||
if (slot is DynamicSlot) {
|
||||
slot.draw(guiGraphics, i-cx, j-cy)
|
||||
}
|
||||
}
|
||||
|
||||
guiGraphics.pose().popPose()
|
||||
}
|
||||
|
||||
open fun renderbg(guiGraphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {}
|
||||
|
||||
open fun renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) { }
|
||||
|
||||
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
|
||||
super.render(graphics, mouseX, mouseY, something)
|
||||
|
||||
graphics.pose().pushPose()
|
||||
graphics.pose().translate(imageX.toFloat(), imageY.toFloat(), 0f)
|
||||
renderCustomOverlay(graphics, mouseX-imageX, mouseY-imageY, something)
|
||||
graphics.pose().popPose() // not even doing this because it's better anymore, im just doing this because i dont want to change it back
|
||||
|
||||
if(shouldRenderTooltip()) super.renderTooltip(graphics, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
super.mouseClicked(mouseX, mouseY, button)
|
||||
for (widget in widgets) {
|
||||
if (widget.mouseClicked(mouseX-imageX, mouseY-imageY, button)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun addWidget(widget: AbstractWidget) {
|
||||
widgets.add(widget)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.neoflock.neocomputers.utils
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import kotlin.math.min
|
||||
|
||||
data class GPUChar(val c: Char, val fg: Int =0xFFFFFF, val bg: Int = 0) // all is bgr
|
||||
|
||||
// TODO: wrapper over NN buffer
|
||||
class TextBuffer(var width: Int, var height: Int) {
|
||||
val blank = GPUChar(' ')
|
||||
var buf = Array(width*height) { blank }
|
||||
|
||||
fun encodeContents(buf: FriendlyByteBuf) {
|
||||
// 0x01 means set fg, 0x02 means set bg,
|
||||
// 0x03 means set char+count
|
||||
var lastFg = 0xFFFFFF
|
||||
var lastBg = 0x000000
|
||||
|
||||
buf.writeVarInt(width)
|
||||
buf.writeVarInt(height)
|
||||
|
||||
var i = 0
|
||||
while(i < this.buf.size) {
|
||||
val px = this.buf[i]
|
||||
if(px.fg != lastFg) {
|
||||
buf.writeByte(0x01)
|
||||
buf.writeVarInt(px.fg)
|
||||
lastFg = px.fg
|
||||
}
|
||||
if(px.bg != lastBg) {
|
||||
buf.writeByte(0x02)
|
||||
buf.writeVarInt(px.bg)
|
||||
lastBg = px.bg
|
||||
}
|
||||
var charWritten = 1
|
||||
while((i+charWritten) < this.buf.size && this.buf[i+charWritten].c == px.c) charWritten++
|
||||
buf.writeByte(0x03)
|
||||
buf.writeVarInt(px.c.code)
|
||||
buf.writeVarInt(charWritten)
|
||||
i += charWritten
|
||||
}
|
||||
}
|
||||
|
||||
fun decodeContents(buf: FriendlyByteBuf) {
|
||||
var lastFg = 0xFFFFFF
|
||||
var lastBg = 0x000000
|
||||
|
||||
width = buf.readVarInt()
|
||||
height = buf.readVarInt()
|
||||
|
||||
if(width*height != this.buf.size) {
|
||||
this.buf = Array(width * height) { blank }
|
||||
}
|
||||
|
||||
var i = 0
|
||||
while(i < width*height) {
|
||||
val op = buf.readByte().toInt()
|
||||
if(op == 0x01) {
|
||||
lastFg = buf.readVarInt()
|
||||
}
|
||||
if(op == 0x02) {
|
||||
lastBg = buf.readVarInt()
|
||||
}
|
||||
if(op == 0x03) {
|
||||
val c = buf.readVarInt().toChar()
|
||||
val n = buf.readVarInt()
|
||||
|
||||
for(o in 0..<n) {
|
||||
this.buf[i+o] = GPUChar(c, lastFg, lastBg)
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun inBounds(x: Int, y: Int) = x >= 0 && y >= 0 && x < width && y < height
|
||||
fun get(x: Int, y: Int) = if(inBounds(x, y)) buf[x+y*width] else blank
|
||||
fun set(x: Int, y: Int, pixel: GPUChar) {
|
||||
if(!inBounds(x, y)) return
|
||||
buf[x+y*width] = pixel
|
||||
}
|
||||
fun set(x: Int, y: Int, text: String, fg: Int = 0xFFFFFF, bg: Int = 0x000000, vertical: Boolean = false) {
|
||||
for ((i, c) in text.toCharArray().withIndex()) {
|
||||
val cx = if(vertical) x else x + i
|
||||
val cy = if(vertical) y + i else y
|
||||
set(cx, cy, GPUChar(c, fg, bg))
|
||||
}
|
||||
}
|
||||
fun fill(x: Int, y: Int, w: Int, h: Int, pixel: GPUChar = blank) {
|
||||
// turn it into values we can fw
|
||||
val fw = min(w, width)
|
||||
val fh = min(h, height)
|
||||
for(py in y..<y+fh) {
|
||||
for (px in x..<x + fw) {
|
||||
set(px, py, pixel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
modLoader = "javafml"
|
||||
loaderVersion = "*"
|
||||
license = "LGPLv3"
|
||||
issueTrackerURL="https://github.com/NeoFlock/NeoComputers/issues"
|
||||
issueTrackerURL="https://gitea.codersquack.nl/NeoFlock/NeoComputers/issues"
|
||||
|
||||
[[mods]]
|
||||
modId = "neocomputers"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
modLoader = "javafml"
|
||||
loaderVersion = "*"
|
||||
license = "LGPLv3"
|
||||
issueTrackerURL="https://github.com/NeoFlock/NeoComputers/issues"
|
||||
issueTrackerURL="https://gitea.codersquack.nl/NeoFlock/NeoComputers/issues"
|
||||
|
||||
[[mods]]
|
||||
modId = "neocomputers"
|
||||
|
||||
8
src/main/resources/assets/neocomputers/atlases/gui.json
Normal file
8
src/main/resources/assets/neocomputers/atlases/gui.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"sources": [
|
||||
{
|
||||
"type": "single",
|
||||
"source": "neocomputers:block/relay_top"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"multipart": [
|
||||
{ "apply": { "model": "neocomputers:block/cable/cable_center" }},
|
||||
{ "when": { "south": true },
|
||||
"apply": { "model": "neocomputers:block/cable/cable_connection" }},
|
||||
{ "when": { "east": true},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": -90}},
|
||||
{ "when": { "west": true },
|
||||
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": 90}},
|
||||
{ "when": { "north": true },
|
||||
"apply": { "model": "neocomputers:block/cable/cable_connection", "y": 180}},
|
||||
{ "when": { "up": true },
|
||||
"apply": { "model": "neocomputers:block/cable/cable_connection", "x": 90}},
|
||||
{ "when": { "down": true },
|
||||
"apply": { "model": "neocomputers:block/cable/cable_connection", "x": -90}},
|
||||
{
|
||||
"when": {
|
||||
"south": false,
|
||||
"north": false,
|
||||
"east": false,
|
||||
"west": false,
|
||||
"up": false,
|
||||
"down": false
|
||||
},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_caps" }
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": true,
|
||||
"north": false,
|
||||
"east": false,
|
||||
"west": false,
|
||||
"up": false,
|
||||
"down": false
|
||||
},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_cap" }
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": false,
|
||||
"north": true,
|
||||
"east": false,
|
||||
"west": false,
|
||||
"up": false,
|
||||
"down": false
|
||||
},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": 180 }
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": false,
|
||||
"north": false,
|
||||
"east": true,
|
||||
"west": false,
|
||||
"up": false,
|
||||
"down": false
|
||||
},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": -90 }
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": false,
|
||||
"north": false,
|
||||
"east": false,
|
||||
"west": true,
|
||||
"up": false,
|
||||
"down": false
|
||||
},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_cap", "y": 90 }
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": false,
|
||||
"north": false,
|
||||
"east": false,
|
||||
"west": false,
|
||||
"up": true,
|
||||
"down": false
|
||||
},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_cap", "x": 90 }
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": false,
|
||||
"north": false,
|
||||
"east": false,
|
||||
"west": false,
|
||||
"up": false,
|
||||
"down": true
|
||||
},
|
||||
"apply": { "model": "neocomputers:block/cable/cable_cap", "x": -90 }
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user