├── .editorconfig
├── .gitattributes
├── .github
├── test-scala-presence.toml
└── workflows
│ ├── build-and-test.yml
│ ├── release-tags.yml
│ └── test-scala-presence.yml
├── .gitignore
├── CODEOWNERS
├── LICENSE
├── README.md
├── build.gradle
├── dependencies.gradle
├── example_website
├── .htaccess
├── favicon.ico
├── index.php
├── login.css
├── login.html
└── style.css
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── src
└── main
├── java
└── pl
│ └── kuba6000
│ └── ae2webintegration
│ ├── ae2interface
│ ├── AE2WebIntegration.java
│ ├── CraftingMediumTracker.java
│ ├── implementations
│ │ └── AE.java
│ └── mixins
│ │ ├── AE2
│ │ ├── CraftingCPUClusterMixin.java
│ │ ├── CraftingGridCacheMixin.java
│ │ └── implementations
│ │ │ ├── AECraftingCPUClusterMixin.java
│ │ │ ├── AECraftingJobMixin.java
│ │ │ ├── AECraftingPatternDetailsMixin.java
│ │ │ ├── AEGridMixin.java
│ │ │ ├── AEItemListMixin.java
│ │ │ ├── AEItemStackMixin.java
│ │ │ ├── AEMeInventoryItemMixin.java
│ │ │ ├── AEPlayerDataMixin.java
│ │ │ ├── PatternProviderViewableMixin.java
│ │ │ └── service
│ │ │ ├── AECraftingGridMixin.java
│ │ │ ├── AEPathingGridMixin.java
│ │ │ ├── AESecurityGridMixin.java
│ │ │ └── AEStorageGridMixin.java
│ │ └── MixinPlugin.java
│ └── core
│ ├── AE2Controller.java
│ ├── AE2JobTracker.java
│ ├── AE2WebIntegration.java
│ ├── AEMixinCallbacks.java
│ ├── AEWebAPI.java
│ ├── ClientProxy.java
│ ├── CommonProxy.java
│ ├── Config.java
│ ├── FMLEventHandler.java
│ ├── GridData.java
│ ├── PasswordHelper.java
│ ├── WebData.java
│ ├── ae2request
│ ├── IRequest.java
│ ├── async
│ │ ├── GetTracking.java
│ │ ├── GetTrackingHistory.java
│ │ ├── GridSettings.java
│ │ └── IAsyncRequest.java
│ └── sync
│ │ ├── CancelCPU.java
│ │ ├── GetCPU.java
│ │ ├── GetCPUList.java
│ │ ├── GetGridList.java
│ │ ├── GetItems.java
│ │ ├── ISyncedRequest.java
│ │ ├── Job.java
│ │ └── Order.java
│ ├── api
│ ├── AEApi
│ │ ├── AEActionable.java
│ │ └── AEControllerState.java
│ ├── DimensionalCoords.java
│ ├── IAEMixinCallbacks.java
│ ├── IAEWebInterface.java
│ ├── JSON_CompactedItem.java
│ ├── JSON_CompactedJobTrackingInfo.java
│ └── JSON_DetailedItem.java
│ ├── commands
│ └── BaseCommandHandler.java
│ ├── discord
│ └── DiscordManager.java
│ ├── interfaces
│ ├── IAE.java
│ ├── IAECraftingJob.java
│ ├── IAECraftingPatternDetails.java
│ ├── IAEGrid.java
│ ├── IAEMeInventoryItem.java
│ ├── IAEPlayerData.java
│ ├── ICraftingCPUCluster.java
│ ├── IItemList.java
│ ├── IItemStack.java
│ ├── IPatternProviderViewable.java
│ └── service
│ │ ├── IAECraftingGrid.java
│ │ ├── IAEPathingGrid.java
│ │ ├── IAESecurityGrid.java
│ │ └── IAEStorageGrid.java
│ └── utils
│ ├── GSONUtils.java
│ ├── HTTPUtils.java
│ ├── RateLimiter.java
│ └── VersionChecker.java
└── resources
├── LICENSE
├── assets
├── favicon.ico
├── login.html
└── webpage.html
├── mcmod.info
└── mixins.ae2webintegration.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This is the universal Text Editor Configuration
2 | # for all GTNewHorizons projects
3 | # See: https://editorconfig.org/
4 |
5 | root = true
6 |
7 | [*]
8 | charset = utf-8
9 | end_of_line = lf
10 | indent_size = 4
11 | indent_style = space
12 | insert_final_newline = true
13 | trim_trailing_whitespace = true
14 |
15 | [*.{bat,ini}]
16 | end_of_line = crlf
17 |
18 | [*.{dtd,json,info,mcmeta,md,sh,svg,xml,xsd,xsl,yaml,yml}]
19 | indent_size = 2
20 |
21 | [*.lang]
22 | trim_trailing_whitespace = false
23 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 |
3 | *.[jJ][aA][rR] binary
4 |
5 | *.[pP][nN][gG] binary
6 | *.[jJ][pP][gG] binary
7 | *.[jJ][pP][eE][gG] binary
8 | *.[gG][iI][fF] binary
9 | *.[tT][iI][fF] binary
10 | *.[tT][iI][fF][fF] binary
11 | *.[iI][cC][oO] binary
12 | *.[sS][vV][gG] text
13 | *.[eE][pP][sS] binary
14 | *.[xX][cC][fF] binary
15 |
16 | *.[kK][aA][rR] binary
17 | *.[mM]4[aA] binary
18 | *.[mM][iI][dD] binary
19 | *.[mM][iI][dD][iI] binary
20 | *.[mM][pP]3 binary
21 | *.[oO][gG][gG] binary
22 | *.[rR][aA] binary
23 |
24 | *.7[zZ] binary
25 | *.[gG][zZ] binary
26 | *.[tT][aA][rR] binary
27 | *.[tT][gG][zZ] binary
28 | *.[zZ][iI][pP] binary
29 |
30 | *.[tT][cC][nN] binary
31 | *.[sS][oO] binary
32 | *.[dD][lL][lL] binary
33 | *.[dD][yY][lL][iI][bB] binary
34 | *.[pP][sS][dD] binary
35 | *.[tT][tT][fF] binary
36 | *.[oO][tT][fF] binary
37 |
38 | *.[pP][aA][tT][cC][hH] -text
39 |
40 | *.[bB][aA][tT] text eol=crlf
41 | *.[cC][mM][dD] text eol=crlf
42 | *.[pP][sS]1 text eol=crlf
43 |
44 | *[aA][uU][tT][oO][gG][eE][nN][eE][rR][aA][tT][eE][dD]* binary
45 |
--------------------------------------------------------------------------------
/.github/test-scala-presence.toml:
--------------------------------------------------------------------------------
1 | [exclude]
2 | "src/main/java/**/*.java" = "import scala."
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Build and test
3 |
4 | on:
5 | pull_request:
6 | branches: [ master, main ]
7 | push:
8 | branches: [ master, main ]
9 |
10 | jobs:
11 | build-and-test:
12 | uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
13 | secrets: inherit
14 | with:
15 | timeout: 150
16 |
--------------------------------------------------------------------------------
/.github/workflows/release-tags.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Release tagged build
3 |
4 | on:
5 | push:
6 | tags: [ '*' ]
7 |
8 | permissions:
9 | contents: write
10 |
11 | jobs:
12 | release-tags:
13 | uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master
14 | secrets: inherit
15 |
--------------------------------------------------------------------------------
/.github/workflows/test-scala-presence.yml:
--------------------------------------------------------------------------------
1 | name: Test Scala Presence
2 |
3 | on:
4 | pull_request:
5 | branches: [ master, main ]
6 | push:
7 | branches: [ master, main ]
8 |
9 | jobs:
10 | test-scala-presence:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v2
15 | - name: Check file content
16 | uses: mattsb42-meta/not-grep@1.0.0
17 | with:
18 | config-file: ./.github/test-scala-presence.toml
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .settings
3 | /.idea/
4 | /.vscode/
5 | /run/
6 | /build/
7 | /eclipse/
8 | .classpath
9 | .project
10 | /bin/
11 | /config/
12 | /crash-reports/
13 | /logs/
14 | options.txt
15 | /saves/
16 | usernamecache.json
17 | banned-ips.json
18 | banned-players.json
19 | eula.txt
20 | ops.json
21 | server.properties
22 | servers.dat
23 | usercache.json
24 | whitelist.json
25 | /out/
26 | *.iml
27 | *.ipr
28 | *.iws
29 | src/main/resources/mixins.*([!.]).json
30 | *.bat
31 | *.DS_Store
32 | !gradlew.bat
33 | .factorypath
34 | addon.local.gradle
35 | addon.local.gradle.kts
36 | addon.late.local.gradle
37 | addon.late.local.gradle.kts
38 | layout.json
39 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @kuba6000
2 |
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | AE2 Web Integration - Minecraft addon
2 | Copyright (C) 2024 kuba6000
3 |
4 | This library is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 3 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public License
15 | along with this library. If not, see .
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # AE2 Web Integration
4 |
5 | [](https://www.curseforge.com/minecraft/mc-mods/ae2-web-integration) [
6 | ](https://modrinth.com/mod/ae2-web-integration)
7 |
8 |
9 |
10 | An add-on for AE2 that lets you view your terminal in your web browser wherever you are! This also includes viewing orders, cancelling them and even making a new ones! There is also a discord integration (through a webhook)!
11 |
12 | **Now also works on multiple networks and on public server = all players can have their own account!**
13 |
14 | **THE MOD SHOULD ONLY BE INSTALLED ON SERVER SIDE, THERE IS NO ITEMS ADDED IN THE GAME!**
15 |
16 | # Showcase on youtube
17 |
18 | ### Main mod
19 | [](https://www.youtube.com/watch?v=3uey6nuW09g)
20 | ### Discord integration
21 | [](https://www.youtube.com/watch?v=e4a86dBz7NY)
22 |
23 | # How?
24 |
25 | This mod starts a web server on server boot that hosts a simple website that you can access directly, or through API calls
26 |
27 | ## How does web panel know which network to chose from?
28 |
29 | Because the mod is server sided only, there is no additional terminal or block added that could identify the networks,
30 | Networks are identified through wireless access points (or security terminal on older mc versions),
31 | all players who wants to access a specific network through the website, must place an wireless access point on that network,
32 | Additionally, if you access the website through an Admin account, you will have access to all networks with at least one wireless access point (doesn't matter if you placed it)
33 |
34 | On older minecraft versions where there is a security terminal, networks are identified through an security terminal,
35 | all players who wants to access the network should have their bio cards in the security terminal block (wild bio card is ignored for security reasons!)
36 | Admin account will have access to all networks on the server that have security terminal (doesn't matter it has your bio card)
37 |
38 | # Current features
39 |
40 | - AE2 terminal (item list+sorting+filtering)
41 | - **Monitor as many networks as you want!**
42 | - **Public mode, that allows the mod to works on servers with many independent players!**
43 | - Start a new order
44 | - Check any CPU status
45 | - Cancel any CPU order
46 | - Order tracking
47 |
48 | # Gallery
49 |
50 |
51 | Gallery
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | # Security
61 |
62 | ## In public mode,
63 |
64 | all users on your server can create an account on the website and access their AE2 networks,
65 | passwords are saved on the server as a PBKDF2WithHmacSHA1 hash
66 | Once a user is authenticated, server generates a token valid for 1 hour/7 days and uses it to verify all requests after
67 |
68 | ## In public mode disabled,
69 | there is only an admin account which password is set in the config.
70 | token mechanism is the same as in public mode.
71 |
72 | **Note: localhost connections are automatically authenticated by default, you can change that in config!**
73 |
74 | # Requirements
75 |
76 | - An open port if you want to access the service outside your local network (configurable, terminal will be hosted at http://your-server-ip-or-domain:configured-port/ for example: http://server.kuba6000.pl:2324/)
77 |
78 | # How to use
79 |
80 | - Download the latest version for your game version from the releases page
81 | - Drop the mod in your server mods folder (only on the server!) (This also works on single player, but is not recommended)
82 | - Start the server
83 | - You can now find the config in /configs/ae2webintegration/ae2webintegration.toml (or .cfg on older mc versions). Edit the port number and password protection for your needs
84 | - **Disable public mode if you play by yourself**
85 | - [This step is done automatically by config watcher since 1.21.1, you can still force reload if it didn't worked!] Reload the config (/ae2webintegration reload) or restart the server
86 | - Make sure you have opened the configured port (firewall, redirections) if you want to use it on public internet
87 | - Now you can visit http://your-server-ip-or-domain:configured-port/ and login should appear on your browser!
88 | - **There is a default user Admin which password is set in the config**
89 |
90 | # Discord integration
91 |
92 | **Note: Discord integration is working only in public mode disabled!**
93 | - Create a webhook on your discord server and set it in the ae2webintegration config
94 | -
95 |
96 |
97 | # Custom website
98 |
99 | - If you already have a web server and want to host the panel there, you can!
100 | - There is currently no API documentation...
101 | - Check out [Simple proxy site](https://github.com/kuba6000/AE2-Web-Integration/tree/master/example_website) !
102 | - There you can find a simple website written in PHP ready to use, it's just simple proxy to API calls to the AE2 endpoint.
103 |
104 | # Compatibility
105 |
106 | The mod is currently implemented only on 1.21.1 (NEO), 1.20.1, 1.12.2 and 1.7.10 versions, although it might change in the future!
107 | A few remarks about compatibility:
108 | - 1.7.10 version is based on [GTNH fork of AE2](https://github.com/GTNewHorizons/Applied-Energistics-2-Unofficial) and [GTNH fork AE2FC for 1.7.10](https://github.com/GTNewHorizons/AE2FluidCraft-Rework)
109 | - 1.12.2 version is based on [AE2-UEL](https://github.com/AE2-UEL/Applied-Energistics-2) and [AE2FC for 1.12.2](https://github.com/AE2-UEL/AE2FluidCraft-Rework/)
110 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 |
2 | plugins {
3 | id 'com.gtnewhorizons.gtnhconvention'
4 | }
5 |
--------------------------------------------------------------------------------
/dependencies.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Add your dependencies here. Supported configurations:
3 | * - api("group:name:version:classifier"): if you use the types from this dependency in the public API of this mod
4 | * Available at runtime and compiletime for mods depending on this mod
5 | * - implementation("g:n:v:c"): if you need this for internal implementation details of the mod, but none of it is visible via the public API
6 | * Available at runtime but not compiletime for mods depending on this mod
7 | * - compileOnly("g:n:v:c"): if the mod you're building doesn't need this dependency during runtime at all, e.g. for optional mods
8 | * Not available at all for mods depending on this mod, only visible at compiletime for this mod
9 | * - compileOnlyApi("g:n:v:c"): like compileOnly, but also visible at compiletime for mods depending on this mod
10 | * Available at compiletime but not runtime for mods depending on this mod
11 | * - runtimeOnlyNonPublishable("g:n:v:c"): if you want to include a mod in this mod's runClient/runServer runs, but not publish it as a dependency
12 | * Not available at all for mods depending on this mod, only visible at runtime for this mod
13 | * - devOnlyNonPublishable("g:n:v:c"): a combination of runtimeOnlyNonPublishable and compileOnly for dependencies present at both compiletime and runtime,
14 | * but not published as Maven dependencies - useful for RFG-deobfuscated dependencies or local testing
15 | * - runtimeOnly("g:n:v:c"): if you don't need this at compile time, but want it to be present at runtime
16 | * Available at runtime for mods depending on this mod
17 | * - annotationProcessor("g:n:v:c"): mostly for java compiler plugins, if you know you need this, use it, otherwise don't worry
18 | * - testCONFIG("g:n:v:c") - replace CONFIG by one of the above (except api), same as above but for the test sources instead of main
19 | *
20 | * - shadowImplementation("g:n:v:c"): effectively the same as API, but the dependency is included in your jar under a renamed package name
21 | * Requires you to enable usesShadowedDependencies in gradle.properties
22 | *
23 | * - compile("g:n:v:c"): deprecated, replace with "api" (works like the old "compile") or "implementation" (can be more efficient)
24 | *
25 | * You can exclude transitive dependencies (dependencies of the chosen dependency) by appending { transitive = false } if needed,
26 | * but use this sparingly as it can break using your mod as another mod's dependency if you're not careful.
27 | *
28 | * To depend on obfuscated jars you can use `devOnlyNonPublishable(rfg.deobf("dep:spec:1.2.3"))` to fetch an obfuscated jar from maven,
29 | * or `devOnlyNonPublishable(rfg.deobf(project.files("libs/my-mod-jar.jar")))` to use a file.
30 | *
31 | * Gradle names for some of the configuration can be misleading, compileOnlyApi and runtimeOnly both get published as dependencies in Maven, but compileOnly does not.
32 | * The buildscript adds runtimeOnlyNonPublishable to also have a runtime dependency that's not published.
33 | *
34 | * For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph
35 | */
36 | dependencies {
37 | api("com.github.GTNewHorizons:GTNHLib:0.6.39:dev")
38 | api("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-684-GTNH:dev")
39 | api("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.4.114-gtnh:dev")
40 | runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.7.82-GTNH:dev")
41 | }
42 |
--------------------------------------------------------------------------------
/example_website/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine On
2 | RewriteCond %{REQUEST_FILENAME} !-f
3 | RewriteCond %{REQUEST_FILENAME} !-d
4 | RedirectMatch 403 /login\.html$
5 | RewriteRule ^(.*)$ index.php?API=$1 [QSA,L]
--------------------------------------------------------------------------------
/example_website/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kuba6000/AE2-Web-Integration/9aa6e6fe27e58b443b077db3007b48e34e78f9a9/example_website/favicon.ico
--------------------------------------------------------------------------------
/example_website/login.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Roboto:wght@300;400&display=swap');
2 | *,html,body{
3 | margin: 0;
4 | padding: 0;
5 | font-family: "Montserrat", sans-serif;
6 | font-optical-sizing: auto;
7 | font-style: normal;
8 | color: #e0e0e0;
9 | font-weight: 400;
10 | list-style-position: inside;
11 | }
12 | b{
13 | font-weight: 500;
14 | }
15 | body{
16 | background-color: #101219;
17 | color: #e0e0e0;
18 | }
19 | html,body{
20 | height: 100%;
21 | width: 100%;
22 | }
23 | body>h1{
24 | text-align: center;
25 | letter-spacing: 10px;
26 | font-weight: 800;
27 | color: #fff;
28 | font-size: 2.5em;
29 | }
30 | footer {
31 | position: fixed;
32 | Width: 100%;
33 | bottom: 0;
34 | background-color: #9292926b;
35 | padding: 10px;
36 | transition: 0.2s;
37 | }
38 |
39 | footer:hover{
40 | background-color: #929292;
41 | }
42 |
43 | a{
44 | color: #80cbc4;
45 | transition: 0.2s;
46 | }
47 |
48 | a:hover{
49 | color: #a7ffeb;
50 | text-decoration: underline;
51 | }
52 |
53 | #maincontent{
54 | width: 90%;
55 | height: 90%;
56 | margin: 0 auto;
57 | display: flex;
58 | }
59 |
60 | #main{
61 | padding: 20px;
62 | margin: auto;
63 | background-color: #222736;
64 | border: black solid 1px;
65 | border-radius: 5px;
66 |
67 | }
68 |
69 | #mainrow
70 | {
71 | display: flex;
72 | flex-direction: row;
73 | }
74 |
75 | #maininfo{
76 | text-align: center;
77 | }
78 |
79 | input, select {
80 | background-color: #2b3555;
81 | accent-color: #9b59b6;
82 | border: 0;
83 | padding: 5px;
84 | border-radius: 5px;
85 | margin-bottom: 10px;
86 | margin-top: 3px;
87 | }
88 |
89 | input[type='submit'], button{
90 | background-color: #292f47;
91 | border: 0;
92 | border-radius: 5px;
93 | padding: 5px;
94 | transition: 0.2s;
95 | }
96 |
97 | input[type='submit']:hover, button:hover{
98 | background-color: #363e5e;
99 | cursor: pointer;
100 | }
101 |
102 | input[type='checkbox']{
103 | transform: translateY(1px);
104 | }
105 |
106 | form {
107 | width: 25vw;
108 | text-align: center;
109 | padding: 20px;
110 | border-right: rgb(216, 216, 216) dashed 1px;
111 | border-left: rgb(216, 216, 216) dashed 1px;
112 | border-collapse: collapse;
113 | }
114 |
115 | #cookiemonster{
116 | z-index: 1000;
117 | position: fixed;
118 | top: 0;
119 | left: 0;
120 | width: 100%;
121 | height: 100%;
122 | background-color: rgba(0, 0, 0, 0.5);
123 | display: flex;
124 | backdrop-filter: blur(5px);
125 | }
126 | #cookiemonster > section{
127 | margin: auto auto;
128 | display: flex;
129 | flex-direction: column;
130 | text-align: center;
131 | background-color: #222736;
132 | border: black solid 1px;
133 | border-radius: 5px;
134 | padding: 20px;
135 | }
136 | #cookiemonster > section > *{
137 | margin: 3px;
138 | }
139 |
140 | h2{
141 | font-weight: bold;
142 | }
--------------------------------------------------------------------------------
/example_website/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | AE2
11 |
12 |
13 | UNIVERSAL WEB TERMINAL
14 |
15 |
16 | Cookie Notice 🍪
17 | Before you continue, please note that this website uses cookies to:
18 |
19 | Save your terminal preferences
20 | Enable login functionality
21 | Improve your overall experience
22 |
23 | By clicking "Accept", you consent to our use of cookies.
24 | Accept Cookies
25 |
26 |
27 |
54 |
55 |
108 |
109 |
110 |
113 |
114 |
--------------------------------------------------------------------------------
/example_website/style.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Roboto:wght@300;400&display=swap');
2 | *,html,body{
3 | margin: 0;
4 | padding: 0;
5 | font-family: "Montserrat", sans-serif;
6 | font-optical-sizing: auto;
7 | font-style: normal;
8 | color: #e0e0e0;
9 | font-weight: 400;
10 | }
11 | b{
12 | font-weight: 500;
13 | }
14 | body{
15 | background-color: #101219;
16 | color: #e0e0e0;
17 | }
18 | html,body{
19 | height: 100%;
20 | width: 100%;
21 | }
22 | td{
23 | padding: 10px;
24 | word-wrap: break-word;
25 | border: 3px #232e53 solid;
26 | border-radius: 1px;
27 | }
28 | td.active{
29 | background-color: rgb(73, 105, 20);
30 | border-color: rgb(53, 77, 14);
31 | }
32 | td.pending{
33 | background-color: rgb(92, 92, 15);
34 | border-color: rgb(71, 71, 11);
35 | }
36 | td.storage{
37 | background-color: #2b3555;
38 | }
39 | td.missing{
40 | background-color: rgb(112, 0, 0);
41 | border-color: rgb(85, 0, 0);
42 | }
43 | td img{
44 | float: right;
45 | width: 32px;
46 | height: 32px;
47 | }
48 | body>h1{
49 | text-align: center;
50 | letter-spacing: 10px;
51 | font-weight: 800;
52 | color: #fff;
53 | font-size: 2.5em;
54 | }
55 |
56 | #terminalcontainer{
57 | width: 90%;
58 | min-height: 90%;
59 | border: black solid 1px;
60 | margin: 0 auto;
61 | border-collapse: collapse;
62 | display: flex;
63 | }
64 |
65 | #terminalgrid{
66 | width: 90%;
67 | margin: 0 auto;
68 | border: black solid 1px;
69 | border-collapse: collapse;
70 | display: flex;
71 | flex-direction: column;
72 | }
73 | #terminalgrid>*{
74 | display: flex;
75 | flex-direction: column;
76 | margin: 10px;
77 | }
78 |
79 | button{
80 | background-color: #292f47;
81 | border: 0;
82 | border-radius: 5px;
83 | padding: 5px;
84 | transition: 0.2s;
85 | }
86 | button:hover{
87 | background-color: #363e5e;
88 | cursor: pointer;
89 | }
90 |
91 | .redtext{
92 | color:rgb(170, 0, 0);
93 | }
94 |
95 | #terminaltypes{
96 | width: 10%;
97 | min-height: 100%;
98 | border-right: black solid 1px;
99 | float: left;
100 | }
101 | #terminaltypes button{
102 | width: calc(100% - 10px);
103 | margin: 5px 5px;
104 | font-size: 110%;
105 | }
106 |
107 | #terminalwindow{
108 | width: 80%;
109 | float: left;
110 | }
111 |
112 | #terminalheader{
113 | width: calc(100% - 20px);
114 | min-height: 40px;
115 | height: fit-content;
116 | border-bottom: black solid 1px;
117 | font-size: 150%;
118 | padding: 10px;
119 | display: flex;
120 | flex-direction: column;
121 | }
122 | input, select {
123 | background-color: #2b3555;
124 | accent-color: #9b59b6;
125 | border: 0;
126 | padding: 5px;
127 | border-radius: 5px;
128 | }
129 | #terminalheader input{
130 | font-size: 100%;
131 | background-color: #2b3555;
132 | border: 0;
133 | border-radius: 5px;
134 | transition: 0.2s;
135 | padding: 5px;
136 | }
137 | #terminalheader input:hover{
138 | background-color: #384572;
139 | }
140 | #terminalheader button{
141 | font-size: 110%;
142 | float:right;
143 | margin-left: 10px;
144 | }
145 | #terminalcontent{
146 | width: 100%;
147 | min-height: 95%;
148 | }
149 | #terminalcontent table{
150 | width: 100%;
151 | table-layout: fixed;
152 | }
153 | #terminalcontent td{
154 | height: auto;
155 | }
156 | #terminalcontent td.button{
157 | background-color: #2b3555;
158 | border: 0;
159 | border-radius: 2px;
160 | padding: 5px;
161 | transition: 0.2s;
162 | }
163 | #terminalcontent td.button:hover{
164 | background-color: #3b4874;
165 | cursor: pointer;
166 | }
167 | #terminalsubcontainer{
168 | width: 10%;
169 | float: left;
170 | border-left: solid black 1px;
171 | font-size: 110%;
172 | }
173 |
174 | #terminalsubcontainer header{
175 | border-bottom: black dotted 1px;
176 | display: block;
177 | width: 100%;
178 | text-align: center;
179 | font-size: 130%;
180 | font-weight: 500;
181 | margin-bottom: 10px;
182 | }
183 | #terminalsubcontainer section{
184 | text-align: center;
185 | }
186 | #terminalsubcontainer > * > section > *{
187 | margin: 5px auto;
188 | }
189 |
190 | #terminalCPUList button, #terminalCPUListForJob button{
191 | width: 100%;
192 | margin: 0;
193 | border-bottom: black dotted 1px;
194 | word-wrap: break-word;
195 | }
196 | #terminalCPUList button.selected, #terminalCPUListForJob button.selected{
197 | background-color: rgb(0, 137, 155);
198 | }
199 | #terminalCPUListForJob button.invalid{
200 | background-color: rgb(155, 23, 0);
201 | }
202 | #terminalCPUListForJob button.mergable{
203 | background-color: rgb(170, 159, 0);
204 | }
205 | #terminalCPUListForJob button.mergable.selected{
206 | background-color: rgb(0, 165, 194);
207 | }
208 |
209 | #terminalHistoryDetails{
210 | padding: 5px;
211 | }
212 |
213 | #overlay {
214 | position: fixed; /* Sit on top of the page content */
215 | display: none; /* Hidden by default */
216 | width: 100%; /* Full width (cover the whole page) */
217 | height: 100%; /* Full height (cover the whole page) */
218 | top: 0;
219 | left: 0;
220 | right: 0;
221 | bottom: 0;
222 | background-color: rgba(0,0,0,0.5); /* Black background with opacity */
223 | z-index: 2; /* Specify a stack order in case you're using a different order for other elements */
224 | }
225 | #overlay>#overlaytext{
226 | position: absolute;
227 | top: 50%;
228 | left: 50%;
229 | font-size: 50px;
230 | font-weight: 900;
231 | color: #e0e0e0;
232 | transform: translate(-50%,-50%);
233 | -ms-transform: translate(-50%,-50%);
234 | }
235 |
236 | #alertoverlay {
237 | position: fixed;
238 | display: flex;
239 | flex-direction: column;
240 | width: 15%;
241 | height: fit-content;
242 | top: 5px;
243 | left: 5px;
244 | right: 0;
245 | bottom: 0;
246 | background-color: rgba(0, 0, 0, 0);
247 | z-index: 2;
248 | }
249 | #alertoverlay > *{
250 | width: 100%;
251 | border: black solid 1px;
252 | border-radius: 5px;
253 | background-color: #cc404049;
254 | padding: 10px;
255 | cursor: pointer;
256 | }
257 |
258 | #alertoverlay > *::after{
259 | content: "X";
260 | float: right;
261 | color: #e0e0e0;
262 |
263 | }
264 |
265 | #topMessages{
266 | position: fixed;
267 | width: 100%;
268 | text-align: center;
269 | background-color: #929292;
270 | transition: 0.2s;
271 | }
272 |
273 | #topMessages > *{
274 | display: block;
275 | text-align: center;
276 | margin: 10px 0;
277 | font-size: 130%;
278 | }
279 |
280 | #topMessages > * > button{
281 | float: right;
282 | margin-right: 10px;
283 | }
284 |
285 |
286 | footer {
287 | position: fixed;
288 | Width: 100%;
289 | bottom: 0;
290 | background-color: #9292926b;
291 | padding: 10px;
292 | transition: 0.2s;
293 | }
294 |
295 | footer:hover{
296 | background-color: #929292;
297 | }
298 |
299 | .note{
300 | color: rgb(80, 80, 80);
301 | font-style: italic;
302 | font-size: 90%;
303 | }
304 | .note::before{
305 | content: "ⓘ ";
306 | }
307 |
308 | a{
309 | color: #80cbc4;
310 | transition: 0.2s;
311 | }
312 |
313 | a:hover{
314 | color: #a7ffeb;
315 | text-decoration: underline;
316 | }
317 |
318 | option:disabled{
319 | color: rgb(170, 170, 170);
320 | }
321 | option:checked{
322 | color: light-dark(rgb(16, 16, 16), rgb(255, 255, 255));
323 | }
324 |
325 | .minecraftSpecialFormat_0{
326 | color: #000;
327 | }
328 |
329 | .minecraftSpecialFormat_1{
330 | color: #0000AA;
331 | }
332 |
333 | .minecraftSpecialFormat_2{
334 | color: #00AA00;
335 | }
336 |
337 | .minecraftSpecialFormat_3{
338 | color: #00AAAA;
339 | }
340 |
341 | .minecraftSpecialFormat_4{
342 | color: #AA0000;
343 | }
344 |
345 | .minecraftSpecialFormat_5{
346 | color: #AA00AA;
347 | }
348 |
349 | .minecraftSpecialFormat_6{
350 | color: #FFAA00;
351 | }
352 |
353 | .minecraftSpecialFormat_7{
354 | color: #AAAAAA;
355 | }
356 |
357 | .minecraftSpecialFormat_8{
358 | color: #555555;
359 | }
360 |
361 | .minecraftSpecialFormat_9{
362 | color: #5555FF;
363 | }
364 |
365 | .minecraftSpecialFormat_a{
366 | color: #55FF55;
367 | }
368 |
369 | .minecraftSpecialFormat_b{
370 | color: #55FFFFFF;
371 | }
372 |
373 | .minecraftSpecialFormat_c{
374 | color: #FF5555;
375 | }
376 |
377 | .minecraftSpecialFormat_d{
378 | color: #FF55FF;
379 | }
380 |
381 | .minecraftSpecialFormat_e{
382 | color: #FFFF55;
383 | }
384 |
385 | .minecraftSpecialFormat_f{
386 | color: #FFFFFF;
387 | }
388 |
389 | .minecraftSpecialFormat_k{
390 | /* no implementation? */
391 | }
392 |
393 | .minecraftSpecialFormat_l{
394 | font-weight: bold;
395 | }
396 |
397 | .minecraftSpecialFormat_m{
398 | text-decoration: line-through;
399 | }
400 |
401 | .minecraftSpecialFormat_n{
402 | text-decoration: underline;
403 | }
404 |
405 | .minecraftSpecialFormat_o{
406 | font-style: italic;
407 | }
408 |
409 | .minecraftSpecialFormat_r{
410 | color: black;
411 | }
412 |
413 | .collapsible:after {
414 | content: '\02795';
415 | font-size: 13px;
416 | color: #e0e0e0;
417 | margin-left: 5px;
418 | }
419 |
420 | .collapsible.active:after {
421 | content: "\2796";
422 | }
423 |
424 | .mobile-only{
425 | display: none;
426 | }
427 |
428 | @media screen and (max-width: 1300px){
429 | body{
430 | font-size: 12px;
431 | }
432 | }
433 |
434 | @media screen and (max-width: 1000px){
435 | #terminalheader{
436 | border-top: black solid 1px;
437 | }
438 | #terminalheader > * > *{
439 | width: 100%;
440 | float: right;
441 | margin-top: 5px;
442 | margin-left: 10px;
443 | }
444 | #terminalheader:first-child{
445 | text-align: center;
446 | font-size: 200% !important;
447 | font-weight: bold !important;
448 | }
449 | #terminalheader > * > input{
450 | width: calc(100% - 10px);
451 | float: right;
452 | margin-top: 5px;
453 | margin-left: 10px;
454 | }
455 | #terminalsubcontainer{
456 | width: 100%;
457 | float: none;
458 | order: 2;
459 | border-left: none;
460 | border-top: black solid 1px;
461 | }
462 | #terminalsubcontainer header{
463 | border-bottom: none;
464 | display: block;
465 | width: 100%;
466 | text-align: center;
467 | font-size: 130%;
468 | font-weight: 500;
469 | margin-bottom: 10px;
470 | }
471 | #terminaltypes{
472 | width: 100%;
473 | float: none;
474 | order: 1;
475 | }
476 | #terminalwindow{
477 | width: 100%;
478 | float: none;
479 | order: 3;
480 | }
481 | #terminalcontainer{
482 | width: 100%;
483 | display: flex;
484 | flex-direction: column;
485 | }
486 | #terminalgrid {
487 | width: 100%;
488 | display: flex;
489 | flex-direction: column;
490 | }
491 | #terminalgrid > *{
492 | width: calc(100% - 10px) !important;
493 | margin: 5px 5px;
494 | margin-top: 10px !important;
495 | font-size: 150% !important;
496 | }
497 | #terminaltypes button{
498 | width: calc(50% - 10px);
499 | margin: 5px 5px;
500 | float: left;
501 | }
502 | .mobile-only{
503 | display: block;
504 | width: calc(100% - 10px) !important;
505 | margin: 5px 5px;
506 | text-align: center;
507 | margin-top: 10px !important;
508 | font-size: 150% !important;
509 | }
510 | .mobile-hidden{
511 | display: none !important;
512 | }
513 | #terminalOptions > section{
514 | display: none;
515 | }
516 | #terminalsubcontainersettings > section{
517 | display: none;
518 | font-size: 150%;
519 | }
520 | }
521 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # ExampleMod tag to use as Blowdryer (Spotless, etc.) settings version, leave empty to disable.
2 | # LOCAL to test local config updates.
3 | gtnh.settings.blowdryerTag = 0.2.2
4 |
5 | # Human-readable mod name, available for mcmod.info population.
6 | modName = AE2WebIntegration
7 |
8 | # Case-sensitive identifier string, available for mcmod.info population and used for automatic mixin JSON generation.
9 | # Conventionally lowercase.
10 | modId = ae2webintegration
11 |
12 | # Root package of the mod, used to find various classes in other properties,
13 | # mcmod.info substitution, enabling assertions in run tasks, etc.
14 | modGroup = pl.kuba6000.ae2webintegration
15 |
16 | # Whether to use modGroup as the maven publishing group.
17 | # When false, com.github.GTNewHorizons is used.
18 | useModGroupForPublishing = false
19 |
20 | # Updates your build.gradle and settings.gradle automatically whenever an update is available.
21 | autoUpdateBuildScript = false
22 |
23 | # Version of Minecraft to target
24 | minecraftVersion = 1.7.10
25 |
26 | # Version of Minecraft Forge to target
27 | forgeVersion = 10.13.4.1614
28 |
29 | # Specify an MCP channel for dependency deobfuscation and the deobfParams task.
30 | channel = stable
31 |
32 | # Specify an MCP mappings version for dependency deobfuscation and the deobfParams task.
33 | mappingsVersion = 12
34 |
35 | # Defines other MCP mappings for dependency deobfuscation.
36 | remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/
37 |
38 | # Select a default username for testing your mod. You can always override this per-run by running
39 | # `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
40 | developmentEnvironmentUserName = Developer
41 |
42 | # Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
43 | # See https://github.com/bsideup/jabel for details on how this works.
44 | enableModernJavaSyntax = true
45 |
46 | # Enables injecting missing generics into the decompiled source code for a better coding experience.
47 | # Turns most publicly visible List, Map, etc. into proper List, Map types.
48 | enableGenericInjection = true
49 |
50 | # Generate a class with a String field for the mod version named as defined below.
51 | # If generateGradleTokenClass is empty or not missing, no such class will be generated.
52 | # If gradleTokenVersion is empty or missing, the field will not be present in the class.
53 | generateGradleTokenClass = pl.kuba6000.ae2webintegration.Tags
54 |
55 | # Name of the token containing the project's current version to generate/replace.
56 | gradleTokenVersion = VERSION
57 |
58 | # [DEPRECATED]
59 | # Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java
60 | # public static final String VERSION = "GRADLETOKEN_VERSION";
61 | # The string's content will be replaced with your mod's version when compiled. You should use this to specify your mod's
62 | # version in @Mod([...], version = VERSION, [...]).
63 | # Leave these properties empty to skip individual token replacements.
64 | replaceGradleTokenInFile =
65 |
66 | # In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can
67 | # leave this property empty.
68 | # Example value: (apiPackage = api) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.api
69 | apiPackage =
70 |
71 | # Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/META-INF/
72 | # There can be multiple files in a space-separated list.
73 | # Example value: mymodid_at.cfg nei_at.cfg
74 | accessTransformersFile =
75 |
76 | # Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
77 | usesMixins = true
78 |
79 | # Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main.
80 | # This can speed up compile times thanks to not running the mixin annotation processor on all input sources.
81 | # Mixin classes will have access to "main" classes, but not the other way around.
82 | separateMixinSourceSet =
83 |
84 | # Adds some debug arguments like verbose output and class export.
85 | usesMixinDebug = false
86 |
87 | # Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
88 | mixinPlugin = ae2interface.mixins.MixinPlugin
89 |
90 | # Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
91 | mixinsPackage = ae2interface.mixins
92 |
93 | # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
94 | # This parameter is for legacy compatibility only
95 | # Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin
96 | coreModClass =
97 |
98 | # If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class
99 | # that is annotated with @Mod) you want this to be true. When in doubt: leave it on false!
100 | containsMixinsAndOrCoreModOnly = false
101 |
102 | # Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins.
103 | forceEnableMixins = false
104 |
105 | # If enabled, you may use 'shadowCompile' for dependencies. They will be integrated into your jar. It is your
106 | # responsibility to check the license and request permission for distribution if required.
107 | usesShadowedDependencies = false
108 |
109 | # If disabled, won't remove unused classes from shadowed dependencies. Some libraries use reflection to access
110 | # their own classes, making the minimization unreliable.
111 | minimizeShadowedDependencies = true
112 |
113 | # If disabled, won't rename the shadowed classes.
114 | relocateShadowedDependencies = true
115 |
116 | # Adds CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
117 | includeWellKnownRepositories = true
118 |
119 | # A list of repositories to exclude from the includeWellKnownRepositories setting. Should be a space separated
120 | # list of strings, with the acceptable keys being(case does not matter):
121 | # cursemaven
122 | # modrinth
123 | excludeWellKnownRepositories =
124 |
125 | # Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven.
126 | # Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables.
127 | # If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle.
128 | usesMavenPublishing = true
129 |
130 | # Maven repository to publish the mod to.
131 | # mavenPublishUrl = https\://nexus.gtnewhorizons.com/repository/releases/
132 |
133 | # Publishing to Modrinth requires you to set the MODRINTH_TOKEN environment variable to your current Modrinth API token.
134 | #
135 | # The project's ID on Modrinth. Can be either the slug or the ID.
136 | # Leave this empty if you don't want to publish to Modrinth.
137 | modrinthProjectId =
138 |
139 | # The project's relations on Modrinth. You can use this to refer to other projects on Modrinth.
140 | # Syntax: scope1-type1:name1;scope2-type2:name2;...
141 | # Where scope can be one of [required, optional, incompatible, embedded],
142 | # type can be one of [project, version],
143 | # and the name is the Modrinth project or version slug/id of the other mod.
144 | # Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
145 | # Note: UniMixins is automatically set as a required dependency if usesMixins = true.
146 | modrinthRelations =
147 |
148 | # Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
149 | #
150 | # The project's numeric ID on CurseForge. You can find this in the About Project box.
151 | # Leave this empty if you don't want to publish on CurseForge.
152 | curseForgeProjectId =
153 |
154 | # The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
155 | # Syntax: type1:name1;type2:name2;...
156 | # Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
157 | # and the name is the CurseForge project slug of the other mod.
158 | # Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft
159 | # Note: UniMixins is automatically set as a required dependency if usesMixins = true.
160 | curseForgeRelations =
161 |
162 | # Optional parameter to customize the produced artifacts. Use this to preserve artifact naming when migrating older
163 | # projects. New projects should not use this parameter.
164 | # customArchiveBaseName =
165 |
166 | # Optional parameter to have the build automatically fail if an illegal version is used.
167 | # This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
168 | # The check is ONLY performed if the version is a git tag.
169 | # Note: the specified string must be escaped, so e.g. 1\\.1\\.\\d+ instead of 1\.1\.\d+
170 | # versionPattern =
171 |
172 | # Uncomment to prevent the source code from being published.
173 | # noPublishedSources = true
174 |
175 | # Uncomment this to disable Spotless checks.
176 | # This should only be uncommented to keep it easier to sync with upstream/other forks.
177 | # That is, if there is no other active fork/upstream, NEVER change this.
178 | # disableSpotless = true
179 |
180 | # Uncomment this to disable Checkstyle checks (currently wildcard import check).
181 | # disableCheckstyle = true
182 |
183 | # Override the IDEA build type. Valid values are: "" (leave blank, do not override), "idea" (force use native IDEA build), "gradle"
184 | # (force use delegated build).
185 | # This is meant to be set in $HOME/.gradle/gradle.properties.
186 | # e.g. add "systemProp.org.gradle.project.ideaOverrideBuildType=idea" will override the build type to be native build.
187 | # WARNING: If you do use this option, it will overwrite whatever you have in your existing projects. This might not be what you want!
188 | # Usually there is no need to uncomment this here as other developers do not necessarily use the same build type as you.
189 | # ideaOverrideBuildType = idea
190 |
191 | # Whether IDEA should run spotless checks when pressing the Build button.
192 | # This is meant to be set in $HOME/.gradle/gradle.properties.
193 | # ideaCheckSpotlessOnBuild = true
194 |
195 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kuba6000/AE2-Web-Integration/9aa6e6fe27e58b443b077db3007b48e34e78f9a9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | if ! command -v java >/dev/null 2>&1
137 | then
138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
139 |
140 | Please set the JAVA_HOME variable in your environment to match the
141 | location of your Java installation."
142 | fi
143 | fi
144 |
145 | # Increase the maximum file descriptors if we can.
146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
147 | case $MAX_FD in #(
148 | max*)
149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
150 | # shellcheck disable=SC2039,SC3045
151 | MAX_FD=$( ulimit -H -n ) ||
152 | warn "Could not query maximum file descriptor limit"
153 | esac
154 | case $MAX_FD in #(
155 | '' | soft) :;; #(
156 | *)
157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
158 | # shellcheck disable=SC2039,SC3045
159 | ulimit -n "$MAX_FD" ||
160 | warn "Could not set maximum file descriptor limit to $MAX_FD"
161 | esac
162 | fi
163 |
164 | # Collect all arguments for the java command, stacking in reverse order:
165 | # * args from the command line
166 | # * the main class name
167 | # * -classpath
168 | # * -D...appname settings
169 | # * --module-path (only if needed)
170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
171 |
172 | # For Cygwin or MSYS, switch paths to Windows format before running java
173 | if "$cygwin" || "$msys" ; then
174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
176 |
177 | JAVACMD=$( cygpath --unix "$JAVACMD" )
178 |
179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
180 | for arg do
181 | if
182 | case $arg in #(
183 | -*) false ;; # don't mess with options #(
184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
185 | [ -e "$t" ] ;; #(
186 | *) false ;;
187 | esac
188 | then
189 | arg=$( cygpath --path --ignore --mixed "$arg" )
190 | fi
191 | # Roll the args list around exactly as many times as the number of
192 | # args, so each arg winds up back in the position where it started, but
193 | # possibly modified.
194 | #
195 | # NB: a `for` loop captures its iteration list before it begins, so
196 | # changing the positional parameters here affects neither the number of
197 | # iterations, nor the values presented in `arg`.
198 | shift # remove old arg
199 | set -- "$@" "$arg" # push replacement arg
200 | done
201 | fi
202 |
203 |
204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
206 |
207 | # Collect all arguments for the java command:
208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
209 | # and any embedded shellness will be escaped.
210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
211 | # treated as '${Hostname}' itself on the command line.
212 |
213 | set -- \
214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
215 | -classpath "$CLASSPATH" \
216 | org.gradle.wrapper.GradleWrapperMain \
217 | "$@"
218 |
219 | # Stop when "xargs" is not available.
220 | if ! command -v xargs >/dev/null 2>&1
221 | then
222 | die "xargs is not available"
223 | fi
224 |
225 | # Use "xargs" to parse quoted args.
226 | #
227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
228 | #
229 | # In Bash we could simply go:
230 | #
231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
232 | # set -- "${ARGS[@]}" "$@"
233 | #
234 | # but POSIX shell has neither arrays nor command substitution, so instead we
235 | # post-process each arg (as a line of input to sed) to backslash-escape any
236 | # character that might be a shell metacharacter, then use eval to reverse
237 | # that process (while maintaining the separation between arguments), and wrap
238 | # the whole thing up as a single "set" statement.
239 | #
240 | # This will of course break if any of these variables contains a newline or
241 | # an unmatched quote.
242 | #
243 |
244 | eval "set -- $(
245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
246 | xargs -n1 |
247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
248 | tr '\n' ' '
249 | )" '"$@"'
250 |
251 | exec "$JAVACMD" "$@"
252 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/repositories.gradle:
--------------------------------------------------------------------------------
1 | // Add any additional repositories for your dependencies here.
2 |
3 | repositories {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 |
2 | pluginManagement {
3 | repositories {
4 | maven {
5 | // RetroFuturaGradle
6 | name "GTNH Maven"
7 | url "https://nexus.gtnewhorizons.com/repository/public/"
8 | mavenContent {
9 | includeGroup("com.gtnewhorizons")
10 | includeGroupByRegex("com\\.gtnewhorizons\\..+")
11 | }
12 | }
13 | gradlePluginPortal()
14 | mavenCentral()
15 | mavenLocal()
16 | }
17 | }
18 |
19 | plugins {
20 | id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/AE2WebIntegration.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface;
2 |
3 | import org.apache.logging.log4j.LogManager;
4 | import org.apache.logging.log4j.Logger;
5 |
6 | import appeng.me.cache.SecurityCache;
7 | import cpw.mods.fml.common.Mod;
8 | import cpw.mods.fml.common.event.FMLInitializationEvent;
9 | import cpw.mods.fml.common.event.FMLPostInitializationEvent;
10 | import cpw.mods.fml.common.event.FMLPreInitializationEvent;
11 | import cpw.mods.fml.common.event.FMLServerStartingEvent;
12 | import pl.kuba6000.ae2webintegration.Tags;
13 | import pl.kuba6000.ae2webintegration.ae2interface.implementations.AE;
14 | import pl.kuba6000.ae2webintegration.core.api.IAEWebInterface;
15 |
16 | @Mod(
17 | modid = AE2WebIntegration.MODID,
18 | version = Tags.VERSION,
19 | name = "AE2WebIntegration-Interface",
20 | acceptedMinecraftVersions = "[1.7.10]",
21 | acceptableRemoteVersions = "*")
22 | public class AE2WebIntegration {
23 |
24 | public static final String MODID = "ae2webintegration-interface";
25 | public static final Logger LOG = LogManager.getLogger(MODID);
26 |
27 | @Mod.EventHandler
28 | public void preInit(FMLPreInitializationEvent event) {}
29 |
30 | @Mod.EventHandler
31 | public void init(FMLInitializationEvent event) {
32 | IAEWebInterface.getInstance()
33 | .initAEInterface(AE.instance);
34 | }
35 |
36 | @Mod.EventHandler
37 | public void postInit(FMLPostInitializationEvent event) {
38 | SecurityCache.registerOpPlayer(
39 | IAEWebInterface.getInstance()
40 | .getAEWebGameProfile());
41 | }
42 |
43 | @Mod.EventHandler
44 | public void serverStarting(FMLServerStartingEvent event) {
45 |
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/CraftingMediumTracker.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface;
2 |
3 | import java.util.IdentityHashMap;
4 |
5 | import appeng.api.networking.IGrid;
6 | import appeng.api.networking.crafting.ICraftingMedium;
7 | import appeng.api.networking.crafting.ICraftingProvider;
8 | import appeng.api.util.IInterfaceViewable;
9 | import appeng.me.cache.CraftingGridCache;
10 |
11 | public class CraftingMediumTracker {
12 |
13 | public static final IdentityHashMap> mediumToViewable = new IdentityHashMap<>();
14 | private static boolean isUpdatingPatterns = false;
15 | private static ICraftingProvider currentCraftingProvider = null;
16 |
17 | public static void updatingPatterns(CraftingGridCache craftingGrid, IGrid grid) {
18 | mediumToViewable.put(grid, new IdentityHashMap<>());
19 | isUpdatingPatterns = true;
20 | }
21 |
22 | public static void provideCrafting(CraftingGridCache craftingGrid, IGrid grid, ICraftingProvider provider) {
23 | if (!isUpdatingPatterns) return;
24 | currentCraftingProvider = provider;
25 | }
26 |
27 | public static void addCraftingOption(CraftingGridCache craftingGrid, IGrid grid, ICraftingMedium medium) {
28 | if (!isUpdatingPatterns) return;
29 | if (currentCraftingProvider == null) return;
30 | if (currentCraftingProvider instanceof IInterfaceViewable viewable && !mediumToViewable.get(grid)
31 | .containsKey(medium)) mediumToViewable.get(grid)
32 | .put(medium, viewable);
33 | }
34 |
35 | public static void doneUpdatingPatterns(CraftingGridCache craftingGrid, IGrid grid) {
36 | isUpdatingPatterns = false;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/implementations/AE.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.implementations;
2 |
3 | import java.util.Iterator;
4 |
5 | import appeng.api.AEApi;
6 | import appeng.core.worlddata.WorldData;
7 | import appeng.hooks.TickHandler;
8 | import appeng.me.Grid;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.IAE;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEPlayerData;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
13 |
14 | public class AE implements IAE {
15 |
16 | public static AE instance = new AE();
17 |
18 | public static AE getInstance() {
19 | return instance;
20 | }
21 |
22 | static class AEGridIterable implements Iterable {
23 |
24 | @Override
25 | public java.util.Iterator iterator() {
26 | return new java.util.Iterator<>() {
27 |
28 | private final Iterator iterator = TickHandler.INSTANCE.getGridList()
29 | .iterator();
30 |
31 | @Override
32 | public boolean hasNext() {
33 | return iterator.hasNext();
34 | }
35 |
36 | @Override
37 | public IAEGrid next() {
38 | return (IAEGrid) iterator.next();
39 | }
40 | };
41 | }
42 | }
43 |
44 | @Override
45 | public Iterable web$getGrids() {
46 | return new AEGridIterable();
47 | }
48 |
49 | @Override
50 | public IItemList web$createItemList() {
51 | return (IItemList) (Object) AEApi.instance()
52 | .storage()
53 | .createItemList();
54 | }
55 |
56 | @Override
57 | public IAEPlayerData web$getPlayerData() {
58 | return (IAEPlayerData) WorldData.instance()
59 | .playerData();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPUClusterMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2;
2 |
3 | import java.util.Map;
4 |
5 | import net.minecraft.inventory.InventoryCrafting;
6 |
7 | import org.spongepowered.asm.mixin.Mixin;
8 | import org.spongepowered.asm.mixin.Shadow;
9 | import org.spongepowered.asm.mixin.injection.At;
10 | import org.spongepowered.asm.mixin.injection.Inject;
11 | import org.spongepowered.asm.mixin.injection.Redirect;
12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
14 |
15 | import com.llamalad7.mixinextras.sugar.Local;
16 |
17 | import appeng.api.networking.IGrid;
18 | import appeng.api.networking.crafting.ICraftingMedium;
19 | import appeng.api.networking.crafting.ICraftingPatternDetails;
20 | import appeng.api.storage.data.IAEItemStack;
21 | import appeng.api.storage.data.IAEStack;
22 | import appeng.api.util.IInterfaceViewable;
23 | import appeng.me.cluster.implementations.CraftingCPUCluster;
24 | import pl.kuba6000.ae2webintegration.ae2interface.CraftingMediumTracker;
25 | import pl.kuba6000.ae2webintegration.core.api.IAEMixinCallbacks;
26 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails;
27 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
28 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
29 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
30 | import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable;
31 |
32 | @Mixin(value = CraftingCPUCluster.class, remap = false)
33 | public class CraftingCPUClusterMixin {
34 |
35 | @Shadow
36 | private IGrid getGrid() {
37 | throw new IllegalStateException("Mixin failed to apply");
38 | }
39 |
40 | @Shadow
41 | private void postCraftingStatusChange(final IAEItemStack diff) {
42 | throw new IllegalStateException("Mixin failed to apply");
43 | }
44 |
45 | @Inject(method = "postCraftingStatusChange", at = @At("HEAD"))
46 | void ae2webintegration$postCraftingStatusChange(IAEItemStack diff, CallbackInfo ci) {
47 | IAEMixinCallbacks.getInstance()
48 | .craftingStatusPostedUpdate((ICraftingCPUCluster) this, (IItemStack) diff);
49 | }
50 |
51 | @Inject(method = "completeJob", at = @At("HEAD"))
52 | void ae2webintegration$completeJob(CallbackInfo ci) {
53 | IAEMixinCallbacks.getInstance()
54 | .jobCompleted((IAEGrid) getGrid(), (ICraftingCPUCluster) this);
55 | }
56 |
57 | @Inject(method = "cancel", at = @At("HEAD"))
58 | void ae2webintegration$cancel(CallbackInfo ci) {
59 | IAEMixinCallbacks.getInstance()
60 | .jobCancelled((IAEGrid) getGrid(), (ICraftingCPUCluster) this);
61 | }
62 |
63 | @Inject(
64 | method = "injectItems",
65 | at = @At(
66 | value = "INVOKE",
67 | target = "Lappeng/api/storage/data/IAEItemStack;setStackSize(J)Lappeng/api/storage/data/IAEStack;",
68 | shift = At.Shift.AFTER,
69 | ordinal = 2))
70 | void ae2webintegration$fixCpuCluster(CallbackInfoReturnable cir, @Local(ordinal = 1) IAEItemStack is) {
71 | postCraftingStatusChange(is);
72 | }
73 |
74 | @Redirect(
75 | method = "executeCrafting",
76 | at = @At(
77 | value = "INVOKE",
78 | target = "Lappeng/api/networking/crafting/ICraftingMedium;pushPattern(Lappeng/api/networking/crafting/ICraftingPatternDetails;Lnet/minecraft/inventory/InventoryCrafting;)Z"))
79 | private boolean ae2webintegration$pushPattern(ICraftingMedium medium, ICraftingPatternDetails details,
80 | InventoryCrafting ic) {
81 | if (medium.pushPattern(details, ic)) {
82 | IInterfaceViewable viewable = null;
83 | Map mediumToViewable = CraftingMediumTracker.mediumToViewable
84 | .get(getGrid());
85 | if (mediumToViewable != null) {
86 | viewable = mediumToViewable.get(medium);
87 | }
88 | IAEMixinCallbacks.getInstance()
89 | .pushedPattern(
90 | (ICraftingCPUCluster) this,
91 | (IPatternProviderViewable) viewable,
92 | (IAECraftingPatternDetails) details);
93 | return true;
94 | }
95 | return false;
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingGridCacheMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2;
2 |
3 | import org.spongepowered.asm.mixin.Final;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Shadow;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Inject;
8 | import org.spongepowered.asm.mixin.injection.Redirect;
9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
10 |
11 | import appeng.api.networking.IGrid;
12 | import appeng.api.networking.crafting.ICraftingJob;
13 | import appeng.api.networking.crafting.ICraftingLink;
14 | import appeng.api.networking.crafting.ICraftingMedium;
15 | import appeng.api.networking.crafting.ICraftingPatternDetails;
16 | import appeng.api.networking.crafting.ICraftingProvider;
17 | import appeng.api.networking.crafting.ICraftingProviderHelper;
18 | import appeng.api.networking.crafting.ICraftingRequester;
19 | import appeng.api.networking.security.BaseActionSource;
20 | import appeng.me.cache.CraftingGridCache;
21 | import appeng.me.cluster.implementations.CraftingCPUCluster;
22 | import pl.kuba6000.ae2webintegration.ae2interface.CraftingMediumTracker;
23 | import pl.kuba6000.ae2webintegration.core.api.IAEMixinCallbacks;
24 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
25 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
26 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
27 |
28 | @Mixin(value = CraftingGridCache.class, remap = false)
29 | public class CraftingGridCacheMixin {
30 |
31 | @Final
32 | @Shadow
33 | private IGrid grid;
34 |
35 | @Redirect(
36 | method = "submitJob(Lappeng/api/networking/crafting/ICraftingJob;Lappeng/api/networking/crafting/ICraftingRequester;Lappeng/api/networking/crafting/ICraftingCPU;ZLappeng/api/networking/security/BaseActionSource;Z)Lappeng/api/networking/crafting/ICraftingLink;",
37 | at = @At(
38 | value = "INVOKE",
39 | target = "Lappeng/me/cluster/implementations/CraftingCPUCluster;submitJob(Lappeng/api/networking/IGrid;Lappeng/api/networking/crafting/ICraftingJob;Lappeng/api/networking/security/BaseActionSource;Lappeng/api/networking/crafting/ICraftingRequester;)Lappeng/api/networking/crafting/ICraftingLink;"))
40 | ICraftingLink ae2webintegration$submitJob(CraftingCPUCluster instance, IGrid craftID, ICraftingJob whatLink,
41 | BaseActionSource list, ICraftingRequester e) {
42 | boolean isMerging = false;
43 | if (instance.isBusy()) {
44 | isMerging = true;
45 | }
46 | ICraftingLink link = instance.submitJob(craftID, whatLink, list, e);
47 | if (link != null) { // job started successfully
48 | boolean isMachine = e != null || list.isMachine();
49 | IAEMixinCallbacks.getInstance()
50 | .jobStarted(
51 | (ICraftingCPUCluster) (Object) instance,
52 | (IAECraftingGrid) this,
53 | (IAEGrid) grid,
54 | isMerging,
55 | !isMachine);
56 | }
57 | return link;
58 | }
59 |
60 | @Inject(
61 | method = "updatePatterns",
62 | at = @At(value = "INVOKE", target = "Ljava/util/Map;clear()V", ordinal = 0, shift = At.Shift.AFTER))
63 | void ae2webintegration$updatePatternsStart(CallbackInfo ci) {
64 | CraftingMediumTracker.updatingPatterns((CraftingGridCache) (Object) this, grid);
65 | }
66 |
67 | @Redirect(
68 | method = "updatePatterns",
69 | at = @At(
70 | value = "INVOKE",
71 | target = "Lappeng/api/networking/crafting/ICraftingProvider;provideCrafting(Lappeng/api/networking/crafting/ICraftingProviderHelper;)V"))
72 | void ae2webintegration$provideCrafting(ICraftingProvider instance,
73 | ICraftingProviderHelper iCraftingProviderHelper) {
74 | CraftingMediumTracker.provideCrafting((CraftingGridCache) (Object) this, grid, instance);
75 | instance.provideCrafting(iCraftingProviderHelper);
76 | }
77 |
78 | @Inject(method = "addCraftingOption", at = @At("HEAD"))
79 | void ae2webintegration$addCraftingOption(ICraftingMedium medium, ICraftingPatternDetails api, CallbackInfo ci) {
80 | CraftingMediumTracker.addCraftingOption((CraftingGridCache) (Object) this, grid, medium);
81 | }
82 |
83 | @Inject(method = "updatePatterns", at = @At(value = "TAIL"))
84 | void ae2webintegration$updatePatternsEnd(CallbackInfo ci) {
85 | CraftingMediumTracker.doneUpdatingPatterns((CraftingGridCache) (Object) this, grid);
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingCPUClusterMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 | import org.spongepowered.asm.mixin.Shadow;
5 | import org.spongepowered.asm.mixin.Unique;
6 |
7 | import appeng.api.networking.crafting.CraftingItemList;
8 | import appeng.api.storage.data.IAEItemStack;
9 | import appeng.me.cluster.implementations.CraftingCPUCluster;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
13 |
14 | @Mixin(value = CraftingCPUCluster.class, remap = false)
15 | public abstract class AECraftingCPUClusterMixin implements ICraftingCPUCluster {
16 |
17 | @Shadow
18 | private appeng.api.storage.data.IItemList waitingFor;
19 |
20 | @Unique
21 | private int web$internalID = -1;
22 |
23 | @Override
24 | public void web$setInternalID(int id) {
25 | web$internalID = id;
26 | }
27 |
28 | @Override
29 | public boolean web$hasCustomName() {
30 | return !((CraftingCPUCluster) (Object) this).getName()
31 | .isEmpty();
32 | }
33 |
34 | @Override
35 | public String web$getName() {
36 | return web$hasCustomName() ? ((CraftingCPUCluster) (Object) this).getName() : ("CPU #" + web$internalID);
37 | }
38 |
39 | @Override
40 | public long web$getAvailableStorage() {
41 | return ((CraftingCPUCluster) (Object) this).getAvailableStorage();
42 | }
43 |
44 | @Unique
45 | private boolean web$isUsedStorageAvailable = true;
46 |
47 | @Unique
48 | private boolean web$usedStorageInitialized = false;
49 |
50 | @Override
51 | public long web$getUsedStorage() {
52 | if (!web$usedStorageInitialized) {
53 | web$usedStorageInitialized = true;
54 | try {
55 | appeng.me.cluster.implementations.CraftingCPUCluster.class.getDeclaredMethod("getUsedStorage");
56 | } catch (NoSuchMethodException e) {
57 | web$isUsedStorageAvailable = false;
58 | return -1L;
59 | }
60 | }
61 | if (!web$isUsedStorageAvailable) return -1L;
62 | return ((CraftingCPUCluster) (Object) this).getUsedStorage();
63 | }
64 |
65 | @Override
66 | public long web$getCoProcessors() {
67 | return ((CraftingCPUCluster) (Object) this).getCoProcessors();
68 | }
69 |
70 | @Override
71 | public boolean web$isBusy() {
72 | return ((CraftingCPUCluster) (Object) this).isBusy();
73 | }
74 |
75 | @Override
76 | public void web$cancel() {
77 | ((CraftingCPUCluster) (Object) this).cancel();
78 | }
79 |
80 | @Override
81 | public IItemStack web$getFinalOutput() {
82 | return (IItemStack) ((CraftingCPUCluster) (Object) this).getFinalOutput();
83 | }
84 |
85 | @Override
86 | public void web$getActiveItems(IItemList list) {
87 | ((CraftingCPUCluster) (Object) this)
88 | .getListOfItem((appeng.api.storage.data.IItemList) (Object) list, CraftingItemList.ACTIVE);
89 | }
90 |
91 | @Override
92 | public void web$getPendingItems(IItemList list) {
93 | ((CraftingCPUCluster) (Object) this)
94 | .getListOfItem((appeng.api.storage.data.IItemList) (Object) list, CraftingItemList.PENDING);
95 | }
96 |
97 | @Override
98 | public void web$getStorageItems(IItemList list) {
99 | ((CraftingCPUCluster) (Object) this)
100 | .getListOfItem((appeng.api.storage.data.IItemList) (Object) list, CraftingItemList.STORAGE);
101 | }
102 |
103 | @Override
104 | public IItemList web$getWaitingFor() {
105 | return (IItemList) (Object) waitingFor;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingJobMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.networking.crafting.ICraftingJob;
6 | import appeng.api.storage.data.IAEItemStack;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
9 |
10 | @Mixin(value = ICraftingJob.class, remap = false)
11 | public interface AECraftingJobMixin extends IAECraftingJob {
12 |
13 | @Override
14 | public default boolean web$isSimulation() {
15 | return ((ICraftingJob) (Object) this).isSimulation();
16 | }
17 |
18 | @Override
19 | public default long web$getByteTotal() {
20 | return ((ICraftingJob) (Object) this).getByteTotal();
21 | }
22 |
23 | @Override
24 | public default void web$populatePlan(IItemList plan) {
25 | ((ICraftingJob) (Object) this).populatePlan((appeng.api.storage.data.IItemList) (Object) plan);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingPatternDetailsMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.networking.crafting.ICraftingPatternDetails;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
8 |
9 | @Mixin(value = ICraftingPatternDetails.class, remap = false)
10 | public interface AECraftingPatternDetailsMixin extends IAECraftingPatternDetails {
11 |
12 | @Override
13 | public default IItemStack[] web$getCondensedOutputs() {
14 | return (IItemStack[]) ((ICraftingPatternDetails) (Object) this).getCondensedOutputs();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEGridMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import net.minecraft.util.IChatComponent;
4 | import net.minecraft.world.World;
5 | import net.minecraft.world.WorldServer;
6 | import net.minecraftforge.common.util.FakePlayer;
7 |
8 | import org.spongepowered.asm.mixin.Mixin;
9 | import org.spongepowered.asm.mixin.Unique;
10 |
11 | import appeng.api.networking.IGridHost;
12 | import appeng.api.networking.IGridNode;
13 | import appeng.api.networking.IMachineSet;
14 | import appeng.api.networking.crafting.ICraftingGrid;
15 | import appeng.api.networking.pathing.IPathingGrid;
16 | import appeng.api.networking.security.IActionHost;
17 | import appeng.api.networking.security.ISecurityGrid;
18 | import appeng.api.networking.security.PlayerSource;
19 | import appeng.api.networking.storage.IStorageGrid;
20 | import appeng.me.Grid;
21 | import appeng.parts.reporting.AbstractPartTerminal;
22 | import cpw.mods.fml.common.FMLCommonHandler;
23 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
24 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
25 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
26 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid;
27 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid;
28 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid;
29 |
30 | @Mixin(value = Grid.class, remap = false)
31 | public abstract class AEGridMixin implements IAEGrid {
32 |
33 | @Override
34 | public IAECraftingGrid web$getCraftingGrid() {
35 | return ((Grid) (Object) this).getCache(ICraftingGrid.class);
36 | }
37 |
38 | @Override
39 | public IAEPathingGrid web$getPathingGrid() {
40 | return ((Grid) (Object) this).getCache(IPathingGrid.class);
41 | }
42 |
43 | @Override
44 | public IAEStorageGrid web$getStorageGrid() {
45 | return ((Grid) (Object) this).getCache(IStorageGrid.class);
46 | }
47 |
48 | @Override
49 | public IAESecurityGrid web$getSecurityGrid() {
50 | return ((Grid) (Object) this).getCache(ISecurityGrid.class);
51 | }
52 |
53 | @Override
54 | public boolean web$isEmpty() {
55 | return ((Grid) (Object) this).isEmpty();
56 | }
57 |
58 | @Unique
59 | private Class extends IGridHost> web$lastUsedMachineClass = null;
60 |
61 | @Unique
62 | public IChatComponent web$lastFakePlayerChatMessage;
63 |
64 | @Unique
65 | private PlayerSource web$cachedPlayerSource = null;
66 |
67 | @Override
68 | public Object web$getPlayerSource() {
69 | Grid internalGrid = (Grid) (Object) this;
70 | IMachineSet terminals = null;
71 | if (web$lastUsedMachineClass != null) terminals = internalGrid.getMachines(web$lastUsedMachineClass);
72 | if (web$lastUsedMachineClass == null || terminals.isEmpty()) {
73 | web$lastUsedMachineClass = null;
74 | Iterable> machines = internalGrid.getMachineClasses();
75 | for (Class extends IGridHost> machine : machines) {
76 | if (AbstractPartTerminal.class.isAssignableFrom(machine)
77 | && !(terminals = internalGrid.getMachines(machine)).isEmpty()) {
78 | web$lastUsedMachineClass = machine;
79 | break;
80 | }
81 | }
82 | }
83 | IActionHost actionHost;
84 | World world;
85 | if (web$lastUsedMachineClass == null || terminals.isEmpty()) {
86 | // throw new RuntimeException("There is no terminal in the AE system");
87 | actionHost = null;
88 | world = FMLCommonHandler.instance()
89 | .getMinecraftServerInstance()
90 | .worldServerForDimension(0);
91 | } else {
92 | IGridNode node = terminals.iterator()
93 | .next();
94 | actionHost = (IActionHost) node.getMachine();
95 | world = node.getWorld();
96 | }
97 |
98 | if (web$cachedPlayerSource != null) {
99 | if (web$cachedPlayerSource.via != actionHost) web$cachedPlayerSource = null;
100 | else return web$cachedPlayerSource;
101 | }
102 |
103 | web$cachedPlayerSource = new PlayerSource(
104 | new FakePlayer((WorldServer) world, AE2Controller.AEControllerProfile) {
105 |
106 | @Override
107 | public void addChatMessage(IChatComponent message) {
108 | web$lastFakePlayerChatMessage = message;
109 | }
110 | },
111 | actionHost);
112 |
113 | return web$cachedPlayerSource;
114 | }
115 |
116 | @Override
117 | public IChatComponent web$getLastFakePlayerChatMessage() {
118 | return web$lastFakePlayerChatMessage;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemListMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.storage.data.IAEStack;
6 | import appeng.api.storage.data.IItemContainer;
7 | import appeng.api.storage.data.IItemList;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
9 |
10 | @Mixin(value = IItemList.class, remap = false)
11 | public interface AEItemListMixin
12 | extends IItemContainer, pl.kuba6000.ae2webintegration.core.interfaces.IItemList {
13 |
14 | @Override
15 | default IItemStack web$findPrecise(IItemStack stack) {
16 | return (IItemStack) findPrecise((StackType) stack);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemStackMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.storage.data.IAEItemStack;
6 | import cpw.mods.fml.common.registry.GameRegistry;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
8 |
9 | @Mixin(IAEItemStack.class)
10 | public interface AEItemStackMixin extends IAEItemStack, IItemStack {
11 |
12 | @Override
13 | public default String web$getItemID() {
14 | return GameRegistry.findUniqueIdentifierFor(getItem())
15 | .toString() + ":"
16 | + getItemDamage();
17 | }
18 |
19 | @Override
20 | public default String web$getDisplayName() {
21 | return getItemStack().getDisplayName();
22 | }
23 |
24 | @Override
25 | public default long web$getStackSize() {
26 | return getStackSize();
27 | }
28 |
29 | @Override
30 | public default boolean web$isCraftable() {
31 | return isCraftable();
32 | }
33 |
34 | @Override
35 | public default long web$getCountRequestable() {
36 | return getCountRequestable();
37 | }
38 |
39 | @Override
40 | public default long web$getCountRequestableCrafts() {
41 | return getCountRequestableCrafts();
42 | }
43 |
44 | @Override
45 | public default void web$reset() {
46 | reset();
47 | }
48 |
49 | @Override
50 | public default boolean web$isSameType(IItemStack other) {
51 | return isSameType((IAEItemStack) other);
52 | }
53 |
54 | @Override
55 | public default IItemStack web$copy() {
56 | return (IItemStack) copy();
57 | }
58 |
59 | @Override
60 | public default void web$setStackSize(long size) {
61 | setStackSize(size);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEMeInventoryItemMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.config.Actionable;
6 | import appeng.api.networking.security.BaseActionSource;
7 | import appeng.api.storage.IMEInventory;
8 | import appeng.api.storage.data.IAEStack;
9 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEActionable;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEMeInventoryItem;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
13 |
14 | @Mixin(value = IMEInventory.class)
15 | public interface AEMeInventoryItemMixin extends IAEMeInventoryItem {
16 |
17 | @Override
18 | public default IItemStack web$extractItems(IItemStack stack, AEActionable mode, IAEGrid grid) {
19 | return (IItemStack) ((IMEInventory) (Object) this).extractItems(
20 | (IAEStack) stack,
21 | mode == AEActionable.MODULATE ? Actionable.MODULATE : Actionable.SIMULATE,
22 | (BaseActionSource) grid.web$getPlayerSource());
23 | }
24 |
25 | @Override
26 | public default IItemStack web$getAvailableItem(IItemStack stack) {
27 | return (IItemStack) ((IMEInventory) (Object) this).getAvailableItem((IAEStack) stack);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEPlayerDataMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import java.util.UUID;
4 |
5 | import javax.annotation.Nonnull;
6 |
7 | import org.spongepowered.asm.mixin.Final;
8 | import org.spongepowered.asm.mixin.Mixin;
9 | import org.spongepowered.asm.mixin.Shadow;
10 |
11 | import com.google.common.base.Optional;
12 | import com.mojang.authlib.GameProfile;
13 |
14 | import appeng.core.worlddata.IWorldPlayerMapping;
15 | import cpw.mods.fml.common.FMLCommonHandler;
16 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEPlayerData;
17 |
18 | @Mixin(targets = "appeng.core.worlddata.PlayerData", remap = false)
19 | public class AEPlayerDataMixin implements IAEPlayerData {
20 |
21 | @Shadow
22 | @Final
23 | private IWorldPlayerMapping playerMapping;
24 |
25 | @Shadow
26 | public int getPlayerID(@Nonnull final GameProfile profile) {
27 | throw new UnsupportedOperationException("Mixin failed to apply.");
28 | }
29 |
30 | @Override
31 | public GameProfile web$getPlayerProfile(int playerId) {
32 | Optional maybe = playerMapping.get(playerId);
33 | if (!maybe.isPresent()) return null;
34 | UUID uuid = maybe.get();
35 | // for (final EntityPlayer player : CommonHelper.proxy.getPlayers()) {
36 | // if (player.getUniqueID().equals(uuid)) {
37 | // return player.getGameProfile();
38 | // }
39 | // }
40 | GameProfile p = FMLCommonHandler.instance()
41 | .getMinecraftServerInstance()
42 | .func_152358_ax()
43 | .func_152652_a(uuid);
44 | if (p == null) {
45 | p = new GameProfile(uuid, uuid.toString());
46 | }
47 | return p;
48 | }
49 |
50 | @Override
51 | public int web$getPlayerId(GameProfile id) {
52 | return getPlayerID(id);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/PatternProviderViewableMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.util.DimensionalCoord;
6 | import appeng.api.util.IInterfaceViewable;
7 | import pl.kuba6000.ae2webintegration.core.api.DimensionalCoords;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable;
9 |
10 | @Mixin(value = IInterfaceViewable.class)
11 | public interface PatternProviderViewableMixin extends IPatternProviderViewable {
12 |
13 | @Override
14 | public default String web$getName() {
15 | return ((IInterfaceViewable) (Object) this).getName();
16 | }
17 |
18 | @Override
19 | public default DimensionalCoords web$getLocation() {
20 | DimensionalCoord coord = ((IInterfaceViewable) (Object) this).getLocation();
21 | return new DimensionalCoords(coord.getDimension(), coord.x, coord.y, coord.z);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AECraftingGridMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations.service;
2 |
3 | import java.util.LinkedHashSet;
4 | import java.util.Set;
5 | import java.util.concurrent.Future;
6 |
7 | import net.minecraft.util.IChatComponent;
8 |
9 | import org.spongepowered.asm.mixin.Mixin;
10 |
11 | import com.google.common.collect.ImmutableSet;
12 |
13 | import appeng.api.networking.IGrid;
14 | import appeng.api.networking.crafting.ICraftingCPU;
15 | import appeng.api.networking.crafting.ICraftingGrid;
16 | import appeng.api.networking.crafting.ICraftingJob;
17 | import appeng.api.networking.crafting.ICraftingLink;
18 | import appeng.api.networking.security.BaseActionSource;
19 | import appeng.api.networking.security.PlayerSource;
20 | import appeng.api.storage.data.IAEItemStack;
21 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob;
22 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
23 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
24 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
25 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
26 |
27 | @Mixin(value = ICraftingGrid.class)
28 | public interface AECraftingGridMixin extends IAECraftingGrid {
29 |
30 | @Override
31 | public default int web$getCPUCount() {
32 | return ((ICraftingGrid) (Object) this).getCpus()
33 | .size();
34 | }
35 |
36 | @Override
37 | public default Set web$getCPUs() {
38 | final ImmutableSet aecpus = ((ICraftingGrid) (Object) this).getCpus();
39 | final Set cpus = new LinkedHashSet<>(aecpus.size());
40 | int i = 1;
41 | for (ICraftingCPU cpu : aecpus) {
42 | cpus.add((ICraftingCPUCluster) cpu);
43 | ((ICraftingCPUCluster) cpu).web$setInternalID(i++);
44 | }
45 | return cpus;
46 | }
47 |
48 | @Override
49 | public default Future web$beginCraftingJob(IAEGrid grid, IItemStack stack) {
50 | PlayerSource actionSrc = (PlayerSource) grid.web$getPlayerSource();
51 | final Future job = ((ICraftingGrid) (Object) this)
52 | .beginCraftingJob(actionSrc.player.worldObj, (IGrid) grid, actionSrc, (IAEItemStack) stack, null);
53 | return (Future) (Object) job;
54 | }
55 |
56 | @Override
57 | public default IChatComponent web$submitJob(IAECraftingJob job, ICraftingCPUCluster target, boolean prioritizePower,
58 | IAEGrid grid) {
59 | ICraftingLink link = ((ICraftingGrid) (Object) this).submitJob(
60 | (ICraftingJob) job,
61 | null,
62 | (ICraftingCPU) target,
63 | prioritizePower,
64 | (BaseActionSource) grid.web$getPlayerSource());
65 | if (link != null) return null;
66 | return grid.web$getLastFakePlayerChatMessage();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEPathingGridMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations.service;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.networking.pathing.IPathingGrid;
6 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid;
8 |
9 | @Mixin(value = IPathingGrid.class, remap = false)
10 | public interface AEPathingGridMixin extends IAEPathingGrid {
11 |
12 | @Override
13 | public default boolean web$isNetworkBooting() {
14 | return ((IPathingGrid) (Object) this).isNetworkBooting();
15 | }
16 |
17 | @Override
18 | public default AEControllerState web$getControllerState() {
19 | return switch (((IPathingGrid) (Object) this).getControllerState()) {
20 | case CONTROLLER_CONFLICT -> AEControllerState.CONTROLLER_CONFLICT;
21 | case CONTROLLER_ONLINE -> AEControllerState.CONTROLLER_ONLINE;
22 | case NO_CONTROLLER -> AEControllerState.NO_CONTROLLER;
23 | default -> AEControllerState.UNSUPPORTED;
24 | };
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AESecurityGridMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations.service;
2 |
3 | import java.util.EnumSet;
4 | import java.util.HashMap;
5 |
6 | import org.spongepowered.asm.mixin.Final;
7 | import org.spongepowered.asm.mixin.Mixin;
8 | import org.spongepowered.asm.mixin.Shadow;
9 |
10 | import com.mojang.authlib.GameProfile;
11 |
12 | import appeng.api.config.SecurityPermissions;
13 | import appeng.core.worlddata.WorldData;
14 | import appeng.me.cache.SecurityCache;
15 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEPlayerData;
16 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid;
17 |
18 | @Mixin(value = SecurityCache.class, remap = false)
19 | public class AESecurityGridMixin implements IAESecurityGrid {
20 |
21 | @Shadow
22 | @Final
23 | private HashMap> playerPerms;
24 |
25 | @Override
26 | public boolean web$isAvailable() {
27 | return ((SecurityCache) (Object) this).isAvailable();
28 | }
29 |
30 | @Override
31 | public long web$getSecurityKey() {
32 | return ((SecurityCache) (Object) this).getSecurityKey();
33 | }
34 |
35 | @Override
36 | public int web$getOwner() {
37 | return ((SecurityCache) (Object) this).getOwner();
38 | }
39 |
40 | @Override
41 | public GameProfile web$getOwnerProfile() {
42 | IAEPlayerData playerData = (IAEPlayerData) WorldData.instance()
43 | .playerData();
44 | return playerData.web$getPlayerProfile(web$getOwner());
45 | }
46 |
47 | @Override
48 | public boolean web$hasPermissions(int playerId) {
49 | if (web$getOwner() == playerId) return true;
50 | EnumSet permissions = playerPerms.get(playerId);
51 | if (permissions == null) {
52 | return false;
53 | }
54 | return permissions.containsAll(
55 | EnumSet.of(
56 | SecurityPermissions.BUILD,
57 | SecurityPermissions.EXTRACT,
58 | SecurityPermissions.INJECT,
59 | SecurityPermissions.CRAFT));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEStorageGridMixin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations.service;
2 |
3 | import org.spongepowered.asm.mixin.Mixin;
4 |
5 | import appeng.api.networking.storage.IStorageGrid;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEMeInventoryItem;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid;
9 |
10 | @Mixin(value = IStorageGrid.class)
11 | public interface AEStorageGridMixin extends IAEStorageGrid {
12 |
13 | @Override
14 | public default IItemList web$getItemStorageList() {
15 | return (IItemList) (Object) ((IStorageGrid) (Object) this).getItemInventory()
16 | .getStorageList();
17 | }
18 |
19 | @Override
20 | public default IAEMeInventoryItem web$getItemInventory() {
21 | return (IAEMeInventoryItem) ((IStorageGrid) (Object) this).getItemInventory();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/MixinPlugin.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.ae2interface.mixins;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 | import java.util.Set;
7 |
8 | import org.apache.logging.log4j.LogManager;
9 | import org.apache.logging.log4j.Logger;
10 | import org.spongepowered.asm.lib.tree.ClassNode;
11 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
12 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
13 |
14 | @SuppressWarnings("unused")
15 | public class MixinPlugin implements IMixinConfigPlugin {
16 |
17 | private static final Logger LOG = LogManager.getLogger("AE2WebIntegration mixins");
18 |
19 | @Override
20 | public void onLoad(String mixinPackage) {
21 |
22 | }
23 |
24 | @Override
25 | public String getRefMapperConfig() {
26 | return null;
27 | }
28 |
29 | @Override
30 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
31 | return false;
32 | }
33 |
34 | @Override
35 | public void acceptTargets(Set myTargets, Set otherTargets) {
36 |
37 | }
38 |
39 | @Override
40 | public List getMixins() {
41 |
42 | List mixins = new ArrayList<>(
43 | Arrays.asList(
44 | "AE2.CraftingGridCacheMixin",
45 | "AE2.CraftingCPUClusterMixin",
46 | "AE2.implementations.AEItemStackMixin",
47 | "AE2.implementations.AEItemListMixin",
48 | "AE2.implementations.AECraftingCPUClusterMixin",
49 | "AE2.implementations.AECraftingJobMixin",
50 | "AE2.implementations.AECraftingPatternDetailsMixin",
51 | "AE2.implementations.AEGridMixin",
52 | "AE2.implementations.AEMeInventoryItemMixin",
53 | "AE2.implementations.AEPlayerDataMixin",
54 | "AE2.implementations.PatternProviderViewableMixin",
55 | "AE2.implementations.service.AECraftingGridMixin",
56 | "AE2.implementations.service.AEPathingGridMixin",
57 | "AE2.implementations.service.AEStorageGridMixin",
58 | "AE2.implementations.service.AESecurityGridMixin"));
59 |
60 | LOG.info("MIXING INTO AE2 LETS GOOOOOOOOOOOOOOOOOOOOOOOOO");
61 |
62 | return mixins;
63 | }
64 |
65 | @Override
66 | public void preApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) {
67 |
68 | }
69 |
70 | @Override
71 | public void postApply(String s, ClassNode classNode, String s1, IMixinInfo iMixinInfo) {
72 |
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/AE2WebIntegration.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.MODID;
4 |
5 | import org.apache.logging.log4j.LogManager;
6 | import org.apache.logging.log4j.Logger;
7 |
8 | import cpw.mods.fml.common.Mod;
9 | import cpw.mods.fml.common.SidedProxy;
10 | import cpw.mods.fml.common.event.FMLInitializationEvent;
11 | import cpw.mods.fml.common.event.FMLPostInitializationEvent;
12 | import cpw.mods.fml.common.event.FMLPreInitializationEvent;
13 | import cpw.mods.fml.common.event.FMLServerStartedEvent;
14 | import cpw.mods.fml.common.event.FMLServerStartingEvent;
15 | import cpw.mods.fml.common.event.FMLServerStoppingEvent;
16 | import pl.kuba6000.ae2webintegration.Tags;
17 |
18 | @Mod(
19 | modid = MODID,
20 | version = Tags.VERSION,
21 | name = "AE2WebIntegration-Core",
22 | acceptedMinecraftVersions = "*",
23 | acceptableRemoteVersions = "*")
24 | public class AE2WebIntegration {
25 |
26 | public static final String MODID = "ae2webintegration-core";
27 | public static final Logger LOG = LogManager.getLogger(MODID);
28 |
29 | @SidedProxy(
30 | clientSide = "pl.kuba6000.ae2webintegration.core.ClientProxy",
31 | serverSide = "pl.kuba6000.ae2webintegration.core.CommonProxy")
32 | public static CommonProxy proxy;
33 |
34 | @Mod.EventHandler
35 | public void preInit(FMLPreInitializationEvent event) {
36 | proxy.preInit(event);
37 | }
38 |
39 | @Mod.EventHandler
40 | public void init(FMLInitializationEvent event) {
41 | proxy.init(event);
42 | }
43 |
44 | @Mod.EventHandler
45 | public void postInit(FMLPostInitializationEvent event) {
46 | proxy.postInit(event);
47 | }
48 |
49 | @Mod.EventHandler
50 | public void serverStarting(FMLServerStartingEvent event) {
51 | proxy.serverStarting(event);
52 | }
53 |
54 | @Mod.EventHandler
55 | public void serverStarted(FMLServerStartedEvent event) {
56 | proxy.serverStarted(event);
57 | }
58 |
59 | @Mod.EventHandler
60 | public void serverStarted(FMLServerStoppingEvent event) {
61 | proxy.serverStopping(event);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/AEMixinCallbacks.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import pl.kuba6000.ae2webintegration.core.api.IAEMixinCallbacks;
4 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails;
5 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
10 |
11 | public class AEMixinCallbacks implements IAEMixinCallbacks {
12 |
13 | public static AEMixinCallbacks INSTANCE = new AEMixinCallbacks();
14 |
15 | @Override
16 | public void jobStarted(ICraftingCPUCluster cpuCluster, IAECraftingGrid cache, IAEGrid grid, boolean isMerging,
17 | boolean isAuthorPlayer) {
18 | if (!Config.TRACKING_TRACK_MACHINE_CRAFTING && !isAuthorPlayer) {
19 | return;
20 | }
21 | AE2JobTracker.addJob(cpuCluster, cache, grid, isMerging);
22 | }
23 |
24 | @Override
25 | public void craftingStatusPostedUpdate(ICraftingCPUCluster cpu, IItemStack diff) {
26 | AE2JobTracker.updateCraftingStatus(cpu, diff);
27 | }
28 |
29 | @Override
30 | public void pushedPattern(ICraftingCPUCluster cpu, IPatternProviderViewable provider,
31 | IAECraftingPatternDetails details) {
32 | AE2JobTracker.pushedPattern(cpu, provider, details);
33 | }
34 |
35 | @Override
36 | public void jobCompleted(IAEGrid grid, ICraftingCPUCluster cpu) {
37 | AE2JobTracker.completeCrafting(grid, cpu);
38 | }
39 |
40 | @Override
41 | public void jobCancelled(IAEGrid grid, ICraftingCPUCluster cpu) {
42 | AE2JobTracker.cancelCrafting(grid, cpu);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/AEWebAPI.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import com.mojang.authlib.GameProfile;
4 |
5 | import pl.kuba6000.ae2webintegration.core.api.IAEWebInterface;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.IAE;
7 |
8 | public class AEWebAPI implements IAEWebInterface {
9 |
10 | public static final AEWebAPI INSTANCE = new AEWebAPI();
11 |
12 | @Override
13 | public GameProfile getAEWebGameProfile() {
14 | return AE2Controller.AEControllerProfile;
15 | }
16 |
17 | @Override
18 | public void initAEInterface(IAE ae) {
19 | AE2Controller.AE2Interface = ae;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ClientProxy.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | public class ClientProxy extends CommonProxy {
4 |
5 | // Override CommonProxy methods here, if you want a different behaviour on the client (e.g. registering renders).
6 | // Don't forget to call the super methods as well.
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/CommonProxy.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import cpw.mods.fml.common.FMLCommonHandler;
4 | import cpw.mods.fml.common.event.FMLInitializationEvent;
5 | import cpw.mods.fml.common.event.FMLPostInitializationEvent;
6 | import cpw.mods.fml.common.event.FMLPreInitializationEvent;
7 | import cpw.mods.fml.common.event.FMLServerStartedEvent;
8 | import cpw.mods.fml.common.event.FMLServerStartingEvent;
9 | import cpw.mods.fml.common.event.FMLServerStoppingEvent;
10 | import pl.kuba6000.ae2webintegration.Tags;
11 | import pl.kuba6000.ae2webintegration.core.commands.BaseCommandHandler;
12 | import pl.kuba6000.ae2webintegration.core.discord.DiscordManager;
13 | import pl.kuba6000.ae2webintegration.core.utils.VersionChecker;
14 |
15 | public class CommonProxy {
16 |
17 | // preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the
18 | // GameRegistry." (Remove if not needed)
19 | public void preInit(FMLPreInitializationEvent event) {
20 | Config.init(event.getModConfigurationDirectory());
21 | Config.synchronizeConfiguration();
22 | WebData.loadData();
23 | GridData.loadData();
24 |
25 | AE2WebIntegration.LOG.info("AE2WebIntegration loading at version " + Tags.VERSION);
26 | if (VersionChecker.isOutdated()) AE2WebIntegration.LOG.warn(
27 | "You are not on latest version ! Consider updating to {} at https://github.com/kuba6000/AE2-Web-Integration/releases/latest",
28 | VersionChecker.getLatestTag());
29 |
30 | FMLCommonHandler.instance()
31 | .bus()
32 | .register(new FMLEventHandler());
33 | }
34 |
35 | // load "Do your mod setup. Build whatever data structures you care about. Register recipes." (Remove if not needed)
36 | public void init(FMLInitializationEvent event) {}
37 |
38 | // postInit "Handle interaction with other mods, complete your setup based on this." (Remove if not needed)
39 | public void postInit(FMLPostInitializationEvent event) {
40 |
41 | }
42 |
43 | // register server commands in this event handler (Remove if not needed)
44 | public void serverStarting(FMLServerStartingEvent event) {
45 | event.registerServerCommand(new BaseCommandHandler());
46 | }
47 |
48 | public void serverStarted(FMLServerStartedEvent event) {
49 | AE2Controller.init();
50 | DiscordManager.init();
51 | if (!Config.AE_PUBLIC_MODE && !Config.DISCORD_WEBHOOK.isEmpty()) {
52 | DiscordManager.postMessageNonBlocking(
53 | new DiscordManager.DiscordEmbed("AE2 Web Integration", "Discord integration started!"));
54 | } else if (Config.AE_PUBLIC_MODE && !Config.DISCORD_WEBHOOK.isEmpty()) {
55 | DiscordManager.postMessageNonBlocking(
56 | new DiscordManager.DiscordEmbed(
57 | "AE2 Web Integration",
58 | "Warning!\nDiscord integration webhook is set in the config, but the public mode is enabled!\nDiscord integration will be disabled!",
59 | 15548997));
60 | }
61 | }
62 |
63 | public void serverStopping(FMLServerStoppingEvent event) {
64 | AE2Controller.stopHTTPServer();
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/Config.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import java.io.File;
4 | import java.util.Random;
5 |
6 | import net.minecraftforge.common.config.Configuration;
7 |
8 | public class Config {
9 |
10 | private static File configDirectory;
11 | private static File configFile;
12 |
13 | public static String AE_PASSWORD = new Random().ints(48, 122 + 1)
14 | .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97))
15 | .limit(16)
16 | .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
17 | .toString();
18 | public static int AE_PORT = 2324;
19 | public static boolean ALLOW_NO_PASSWORD_ON_LOCALHOST = true;
20 | public static boolean AE_PUBLIC_MODE = true;
21 | public static int AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE = 20;
22 |
23 | // discord
24 | public static String DISCORD_WEBHOOK = "";
25 | public static String DISCORD_ROLE_ID = "";
26 |
27 | // tracking
28 | // TODO: Add more customization options (order time, size, item type ? etc.)
29 | public static boolean TRACKING_TRACK_MACHINE_CRAFTING = false;
30 |
31 | public static void synchronizeConfiguration() {
32 | Configuration configuration = new Configuration(configFile);
33 | AE_PORT = configuration
34 | .getInt("port", Configuration.CATEGORY_GENERAL, AE_PORT, 1, 65535, "Port for the hosted website");
35 | AE_PASSWORD = configuration
36 | .getString("password", Configuration.CATEGORY_GENERAL, AE_PASSWORD, "Password for the admin account");
37 | ALLOW_NO_PASSWORD_ON_LOCALHOST = configuration.getBoolean(
38 | "allow_no_password_on_localhost",
39 | Configuration.CATEGORY_GENERAL,
40 | ALLOW_NO_PASSWORD_ON_LOCALHOST,
41 | "Don't require to login using loopback address (127.0.0.1/localhost)");
42 | AE_PUBLIC_MODE = configuration.getBoolean(
43 | "public_mode",
44 | Configuration.CATEGORY_GENERAL,
45 | AE_PUBLIC_MODE,
46 | "Public server mode = enable registration system on the website, players will be able to register and login to monitor their own ae networks, "
47 | + "if disabled, there is only one admin account with password set in the config file with access to all networks on the server");
48 | AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE = configuration.getInt(
49 | "max_requests_before_logged_in_per_minute",
50 | Configuration.CATEGORY_GENERAL,
51 | AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE,
52 | 1,
53 | 1000,
54 | "How many requests can be made before user is logged in per minute");
55 |
56 | DISCORD_WEBHOOK = configuration.getString(
57 | "discord_webhook",
58 | "discord",
59 | "",
60 | "Discord webhook url (OPTIONAL, leave empty to ignore) (WORKS ONLY IF PUBLIC_MODE IS DISABLED)");
61 | DISCORD_ROLE_ID = configuration
62 | .getString("discord_role_id", "discord", "", "Role to ping on message (OPTIONAL, leave empty to ignore)");
63 |
64 | TRACKING_TRACK_MACHINE_CRAFTING = configuration.getBoolean(
65 | "track_machine_crafting",
66 | "tracking",
67 | TRACKING_TRACK_MACHINE_CRAFTING,
68 | "Track automated crafting jobs (not ordered by player)");
69 |
70 | if (configuration.hasKey(Configuration.CATEGORY_GENERAL, "cpu_count_threshold")) {
71 | configuration.getInt(
72 | "cpu_count_threshold",
73 | Configuration.CATEGORY_GENERAL,
74 | 0,
75 | 0,
76 | 0,
77 | "[DEPRECATED] This option is no longer used, you can remove it from your config file.");
78 | }
79 |
80 | if (configuration.hasChanged()) {
81 | configuration.save();
82 | }
83 | }
84 |
85 | public static void init(File configDirectory) {
86 | Config.configDirectory = new File(configDirectory, "ae2webintegration");
87 | Config.configFile = new File(Config.configDirectory, "ae2webintegration.cfg");
88 | if (!Config.configDirectory.exists()) {
89 | Config.configDirectory.mkdirs();
90 | File oldConfigFile = new File(configDirectory, "ae2webintegration.cfg");
91 | if (oldConfigFile.exists()) {
92 | oldConfigFile.renameTo(Config.configFile);
93 | }
94 | }
95 |
96 | }
97 |
98 | public static File getConfigFile(String fileName) {
99 | return new File(configDirectory, fileName);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/FMLEventHandler.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import net.minecraft.entity.player.EntityPlayerMP;
4 | import net.minecraft.util.ChatComponentText;
5 | import net.minecraft.util.EnumChatFormatting;
6 |
7 | import cpw.mods.fml.common.eventhandler.SubscribeEvent;
8 | import cpw.mods.fml.common.gameevent.PlayerEvent;
9 | import cpw.mods.fml.common.gameevent.TickEvent;
10 | import pl.kuba6000.ae2webintegration.core.ae2request.sync.ISyncedRequest;
11 | import pl.kuba6000.ae2webintegration.core.utils.VersionChecker;
12 |
13 | public class FMLEventHandler {
14 |
15 | @SubscribeEvent
16 | public void tick(TickEvent.ServerTickEvent event) {
17 | if (event.phase == TickEvent.Phase.START) return;
18 | ++AE2Controller.timer;
19 | if (AE2Controller.timer % 5 == 0) {
20 | while (AE2Controller.requests.peek() != null) {
21 | ISyncedRequest request = AE2Controller.requests.poll();
22 | request.handle(AE2Controller.AE2Interface);
23 | }
24 | }
25 | }
26 |
27 | @SubscribeEvent
28 | public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
29 | if (!(event.player instanceof EntityPlayerMP)) return;
30 | if (VersionChecker.isOutdated() && event.player.canCommandSenderUseCommand(4, "seed"))
31 | event.player.addChatMessage(
32 | new ChatComponentText(
33 | EnumChatFormatting.GREEN.toString() + EnumChatFormatting.BOLD
34 | + "----> AE2WebIntegration -> New version detected! Consider updating at https://github.com/kuba6000/AE2-Web-Integration/releases/latest"));
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/GridData.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.LOG;
4 |
5 | import java.io.File;
6 | import java.io.Reader;
7 | import java.io.Writer;
8 | import java.lang.reflect.Type;
9 | import java.nio.charset.StandardCharsets;
10 | import java.util.HashMap;
11 | import java.util.concurrent.ConcurrentHashMap;
12 | import java.util.concurrent.Future;
13 |
14 | import com.google.common.io.Files;
15 | import com.google.gson.Gson;
16 | import com.google.gson.reflect.TypeToken;
17 |
18 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState;
19 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob;
20 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
21 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid;
22 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid;
23 | import pl.kuba6000.ae2webintegration.core.utils.GSONUtils;
24 |
25 | public class GridData {
26 |
27 | @GSONUtils.SkipGSON
28 | private static final File dataFile = Config.getConfigFile("griddata.json");
29 |
30 | @GSONUtils.SkipGSON
31 | private static ConcurrentHashMap gridDataMap = new ConcurrentHashMap<>();
32 |
33 | public boolean isTracked = false;
34 |
35 | @GSONUtils.SkipGSON
36 | public AE2JobTracker trackingInfo = new AE2JobTracker();
37 |
38 | @GSONUtils.SkipGSON
39 | private int nextJobID = 1;
40 |
41 | private int getNextJobID() {
42 | return nextJobID++;
43 | }
44 |
45 | @GSONUtils.SkipGSON
46 | public HashMap> jobs = new HashMap<>();
47 |
48 | public int addJob(Future job) {
49 | int jobID = getNextJobID();
50 | jobs.put(jobID, job);
51 | return jobID;
52 | }
53 |
54 | public static GridData get(long gridKey) {
55 | return gridDataMap.computeIfAbsent(gridKey, k -> new GridData());
56 | }
57 |
58 | public static GridData get(IAEGrid grid) {
59 | IAEPathingGrid pathing = grid.web$getPathingGrid();
60 | if (pathing == null || pathing.web$isNetworkBooting()
61 | || pathing.web$getControllerState() != AEControllerState.CONTROLLER_ONLINE) {
62 | return null;
63 | }
64 | IAESecurityGrid security = grid.web$getSecurityGrid();
65 | if (security == null || !security.web$isAvailable()) {
66 | return null;
67 | }
68 | long gridKey = security.web$getSecurityKey();
69 | if (gridKey == -1) {
70 | return null;
71 | }
72 | return gridDataMap.computeIfAbsent(gridKey, k -> new GridData());
73 | }
74 |
75 | public static void saveChanges() {
76 | Gson gson = GSONUtils.GSON_BUILDER.create();
77 | Writer writer = null;
78 | try {
79 | writer = Files.newWriter(dataFile, StandardCharsets.UTF_8);
80 | gson.toJson(gridDataMap, writer);
81 | writer.flush();
82 | writer.close();
83 | } catch (Exception e) {
84 | e.printStackTrace();
85 | } finally {
86 | if (writer != null) try {
87 | writer.close();
88 | } catch (Exception ignored) {}
89 | }
90 | }
91 |
92 | public static void loadData() {
93 | Gson gson = GSONUtils.GSON_BUILDER.create();
94 | if (!dataFile.exists()) {
95 | LOG.info("Grid data file not found, creating a new one.");
96 | saveChanges();
97 | return;
98 | }
99 | Reader reader = null;
100 | try {
101 | reader = Files.newReader(dataFile, StandardCharsets.UTF_8);
102 | Type type = new TypeToken>() {}.getType();
103 | gridDataMap = gson.fromJson(reader, type);
104 | } catch (Exception e) {
105 | LOG.error("Failed to load web data from file: {}", dataFile.getAbsolutePath(), e);
106 | gridDataMap.clear();
107 | saveChanges();
108 | } finally {
109 | if (reader != null) try {
110 | reader.close();
111 | } catch (Exception ignored) {}
112 | }
113 |
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/PasswordHelper.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import java.math.BigInteger;
4 | import java.security.NoSuchAlgorithmException;
5 | import java.security.SecureRandom;
6 | import java.security.spec.InvalidKeySpecException;
7 |
8 | import javax.crypto.SecretKeyFactory;
9 | import javax.crypto.spec.PBEKeySpec;
10 |
11 | public class PasswordHelper {
12 |
13 | private static final int ITERATIONS = 65536;
14 | private static final int HASH_LENGTH = 512; // Length of the hash in bytes
15 |
16 | public static String generateStrongPasswordHash(String password)
17 | throws NoSuchAlgorithmException, InvalidKeySpecException {
18 | char[] chars = password.toCharArray();
19 | byte[] salt = getSalt();
20 |
21 | PBEKeySpec spec = new PBEKeySpec(chars, salt, ITERATIONS, HASH_LENGTH);
22 | SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
23 |
24 | byte[] hash = skf.generateSecret(spec)
25 | .getEncoded();
26 | return ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash);
27 | }
28 |
29 | private static byte[] getSalt() throws NoSuchAlgorithmException {
30 | SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
31 | byte[] salt = new byte[16];
32 | sr.nextBytes(salt);
33 | return salt;
34 | }
35 |
36 | private static String toHex(byte[] array) throws NoSuchAlgorithmException {
37 | BigInteger bi = new BigInteger(1, array);
38 | String hex = bi.toString(16);
39 |
40 | int paddingLength = (array.length * 2) - hex.length();
41 | if (paddingLength > 0) {
42 | return String.format("%0" + paddingLength + "d", 0) + hex;
43 | } else {
44 | return hex;
45 | }
46 | }
47 |
48 | public static boolean validatePassword(String originalPassword, String storedPassword)
49 | throws NoSuchAlgorithmException, InvalidKeySpecException {
50 | String[] parts = storedPassword.split(":");
51 | int iterations = Integer.parseInt(parts[0]);
52 |
53 | byte[] salt = fromHex(parts[1]);
54 | byte[] hash = fromHex(parts[2]);
55 |
56 | PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt, iterations, hash.length * 8);
57 | SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
58 | byte[] testHash = skf.generateSecret(spec)
59 | .getEncoded();
60 |
61 | int diff = hash.length ^ testHash.length;
62 | for (int i = 0; i < hash.length && i < testHash.length; i++) {
63 | diff |= hash[i] ^ testHash[i];
64 | }
65 | return diff == 0;
66 | }
67 |
68 | private static byte[] fromHex(String hex) throws NoSuchAlgorithmException {
69 | byte[] bytes = new byte[hex.length() / 2];
70 | for (int i = 0; i < bytes.length; i++) {
71 | bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
72 | }
73 | return bytes;
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/WebData.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core;
2 |
3 | import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.LOG;
4 |
5 | import java.io.File;
6 | import java.io.Reader;
7 | import java.io.Writer;
8 | import java.nio.charset.StandardCharsets;
9 | import java.util.HashMap;
10 | import java.util.UUID;
11 |
12 | import com.google.common.io.Files;
13 | import com.google.gson.Gson;
14 | import com.mojang.authlib.GameProfile;
15 |
16 | import cpw.mods.fml.common.FMLCommonHandler;
17 | import pl.kuba6000.ae2webintegration.core.utils.GSONUtils;
18 |
19 | public class WebData {
20 |
21 | static WebData instance = new WebData();
22 |
23 | private static final File dataFile = Config.getConfigFile("webdata.json");
24 |
25 | private HashMap UUIDToId = new HashMap<>();
26 | private HashMap IdToUUID = new HashMap<>();
27 | private HashMap passwords = new HashMap<>();
28 |
29 | public static int getPlayerId(String name) {
30 | if (name == null || name.isEmpty()) {
31 | return -1;
32 | }
33 | GameProfile profile = FMLCommonHandler.instance()
34 | .getMinecraftServerInstance()
35 | .func_152358_ax()
36 | .func_152655_a(name);
37 | if (profile == null) {
38 | return -1;
39 | }
40 | Integer id = instance.UUIDToId.get(profile.getId());
41 | if (id != null) {
42 | return id;
43 | }
44 |
45 | return -1;
46 | }
47 |
48 | public static boolean verifyPassword(int playerId, String password) {
49 | UUID id = instance.IdToUUID.get(playerId);
50 | if (id == null) {
51 | LOG.warn("Player ID {} not found in IdToUUID map.", playerId);
52 | return false;
53 | }
54 | if (instance.passwords.containsKey(id)) {
55 | try {
56 | return PasswordHelper.validatePassword(password, instance.passwords.get(id));
57 | } catch (Exception e) {
58 | LOG.error("Password verification failed for player ID: {}", playerId, e);
59 | return false;
60 | }
61 | }
62 |
63 | return false;
64 | }
65 |
66 | public static void setPassword(GameProfile playerId, String passwordHash) {
67 | if (passwordHash == null || passwordHash.isEmpty()) {
68 | instance.passwords.remove(playerId.getId());
69 | } else {
70 | try {
71 | instance.passwords.put(playerId.getId(), passwordHash);
72 | int pID = AE2Controller.AE2Interface.web$getPlayerData()
73 | .web$getPlayerId(playerId);
74 | instance.UUIDToId.put(playerId.getId(), pID);
75 | instance.IdToUUID.put(pID, playerId.getId());
76 | } catch (Exception e) {
77 | LOG.error("Failed to set password for player ID: {}", playerId, e);
78 | }
79 | }
80 | saveChanges();
81 | }
82 |
83 | private static void saveChanges() {
84 | Gson gson = GSONUtils.GSON_BUILDER.create();
85 | Writer writer = null;
86 | try {
87 | writer = Files.newWriter(dataFile, StandardCharsets.UTF_8);
88 | gson.toJson(instance, writer);
89 | writer.flush();
90 | writer.close();
91 | } catch (Exception e) {
92 | e.printStackTrace();
93 | } finally {
94 | if (writer != null) try {
95 | writer.close();
96 | } catch (Exception ignored) {}
97 | }
98 | }
99 |
100 | public static void loadData() {
101 | Gson gson = GSONUtils.GSON_BUILDER.create();
102 | if (!dataFile.exists()) {
103 | LOG.info("Web data file not found, creating a new one.");
104 | saveChanges();
105 | return;
106 | }
107 | Reader reader = null;
108 | try {
109 | reader = Files.newReader(dataFile, StandardCharsets.UTF_8);
110 | instance = gson.fromJson(reader, WebData.class);
111 | } catch (Exception e) {
112 | LOG.error("Failed to load web data from file: {}", dataFile.getAbsolutePath(), e);
113 | instance.UUIDToId.clear();
114 | instance.IdToUUID.clear();
115 | instance.passwords.clear();
116 | saveChanges();
117 | } finally {
118 | if (reader != null) try {
119 | reader.close();
120 | } catch (Exception ignored) {}
121 | }
122 |
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/IRequest.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request;
2 |
3 | import java.util.concurrent.atomic.AtomicBoolean;
4 |
5 | import com.google.gson.GsonBuilder;
6 |
7 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
8 | import pl.kuba6000.ae2webintegration.core.utils.GSONUtils;
9 |
10 | public abstract class IRequest {
11 |
12 | protected static GsonBuilder JSONBuilder = GSONUtils.GSON_BUILDER;
13 |
14 | private static class JSON_Structure {
15 |
16 | String status;
17 | Object data;
18 | }
19 |
20 | public AtomicBoolean isDone = new AtomicBoolean(false);
21 | protected String status = "TIMEOUT";
22 | protected Object data = null;
23 |
24 | abstract public void handle(AE2Controller.RequestContext context);
25 |
26 | Object getData() {
27 | return data;
28 | }
29 |
30 | protected void setData(Object data) {
31 | this.data = data;
32 | }
33 |
34 | public String getJSON() {
35 | JSON_Structure structure = new JSON_Structure();
36 | structure.status = status;
37 | structure.data = getData();
38 | return JSONBuilder.create()
39 | .toJson(structure);
40 | }
41 |
42 | public void done() {
43 | this.status = "OK";
44 | this.isDone.set(true);
45 | }
46 |
47 | public void deny(String status) {
48 | this.status = status;
49 | this.isDone.set(true);
50 | }
51 |
52 | public void noParam(String... params) {
53 | deny("NO_PARAM");
54 | setData(params);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTracking.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.async;
2 |
3 | import java.util.Map;
4 |
5 | import pl.kuba6000.ae2webintegration.core.AE2JobTracker;
6 | import pl.kuba6000.ae2webintegration.core.api.JSON_CompactedJobTrackingInfo;
7 |
8 | public class GetTracking extends IAsyncRequest {
9 |
10 | @Override
11 | public void handle(Map getParams) {
12 | if (grid == null) {
13 | deny("GRID_NOT_FOUND");
14 | return;
15 | }
16 | if (!getParams.containsKey("id")) {
17 | noParam("id");
18 | return;
19 | }
20 | int id = Integer.parseInt(getParams.get("id"));
21 |
22 | AE2JobTracker.JobTrackingInfo info = grid.trackingInfo.trackingInfos.get(id);
23 | if (info == null) {
24 | deny("TRACKING_NOT_FOUND");
25 | return;
26 | }
27 |
28 | setData(new JSON_CompactedJobTrackingInfo(info));
29 | done();
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTrackingHistory.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.async;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Map;
5 |
6 | import pl.kuba6000.ae2webintegration.core.AE2JobTracker;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
8 |
9 | public class GetTrackingHistory extends IAsyncRequest {
10 |
11 | private static class JSON_TrackingHistoryElement {
12 |
13 | public long timeStarted;
14 | public long timeDone;
15 | public boolean wasCancelled;
16 | public IItemStack finalOutput;
17 | public int id;
18 | }
19 |
20 | @Override
21 | public void handle(Map getParams) {
22 | if (grid == null) {
23 | deny("GRID_NOT_FOUND");
24 | return;
25 | }
26 | ArrayList jobs = new ArrayList<>(grid.trackingInfo.trackingInfos.size());
27 |
28 | for (Map.Entry integerJobTrackingInfoEntry : grid.trackingInfo.trackingInfos
29 | .entrySet()) {
30 | JSON_TrackingHistoryElement element = new JSON_TrackingHistoryElement();
31 | element.id = integerJobTrackingInfoEntry.getKey();
32 | element.timeStarted = integerJobTrackingInfoEntry.getValue().timeStarted;
33 | element.timeDone = integerJobTrackingInfoEntry.getValue().timeDone;
34 | element.wasCancelled = integerJobTrackingInfoEntry.getValue().wasCancelled;
35 | element.finalOutput = integerJobTrackingInfoEntry.getValue().finalOutput;
36 | jobs.add(element);
37 | }
38 |
39 | jobs.sort((i1, i2) -> Long.compare(i2.timeDone, i1.timeDone));
40 |
41 | setData(jobs);
42 | done();
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GridSettings.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.async;
2 |
3 | import java.util.Map;
4 |
5 | import pl.kuba6000.ae2webintegration.core.GridData;
6 |
7 | public class GridSettings extends IAsyncRequest {
8 |
9 | @Override
10 | public void handle(Map getParams) {
11 | if (grid == null) {
12 | deny("GRID_NOT_FOUND");
13 | return;
14 | }
15 |
16 | if (getParams.containsKey("track")) {
17 | grid.isTracked = getParams.get("track")
18 | .equals("1");
19 | GridData.saveChanges();
20 | }
21 |
22 | setData(grid);
23 | done();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/IAsyncRequest.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.async;
2 |
3 | import java.util.Map;
4 |
5 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
6 | import pl.kuba6000.ae2webintegration.core.GridData;
7 | import pl.kuba6000.ae2webintegration.core.ae2request.IRequest;
8 |
9 | public abstract class IAsyncRequest extends IRequest {
10 |
11 | protected AE2Controller.RequestContext context = null;
12 | protected long gridKey = -1;
13 | protected GridData grid = null;
14 |
15 | public void handle(Map getParams) {};
16 |
17 | @Override
18 | public void handle(AE2Controller.RequestContext context) {
19 | this.context = context;
20 | String gridstr = context.getGetParams()
21 | .get("grid");
22 | if (gridstr == null || gridstr.isEmpty()) gridKey = -1;
23 | else gridKey = Long.parseLong(gridstr);
24 | if (gridKey != -1) {
25 | grid = GridData.get(gridKey);
26 | }
27 | handle(context.getGetParams());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/CancelCPU.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import java.util.Map;
4 |
5 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
7 |
8 | public class CancelCPU extends ISyncedRequest {
9 |
10 | private String cpuName;
11 |
12 | @Override
13 | boolean init(Map getParams) {
14 | if (!getParams.containsKey("cpu")) {
15 | noParam("cpu");
16 | return false;
17 | }
18 | cpuName = getParams.get("cpu");
19 | return true;
20 | }
21 |
22 | @Override
23 | void handle(IAEGrid grid) {
24 | if (grid == null) {
25 | deny("GRID_NOT_FOUND");
26 | return;
27 | }
28 | ICraftingCPUCluster cluster = GetCPUList.getCPUList(grid.web$getCraftingGrid())
29 | .get(cpuName);
30 | if (cluster == null) {
31 | deny("CPU_NOT_FOUND");
32 | return;
33 | }
34 | if (cluster.web$isBusy()) {
35 | cluster.web$cancel();
36 | done();
37 | return;
38 | }
39 | deny("CPU_NOT_BUSY");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPU.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
8 | import pl.kuba6000.ae2webintegration.core.AE2JobTracker;
9 | import pl.kuba6000.ae2webintegration.core.api.JSON_CompactedItem;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
13 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
14 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
15 |
16 | public class GetCPU extends ISyncedRequest {
17 |
18 | private static class JSON_ClusterData {
19 |
20 | public long size;
21 | public boolean isBusy;
22 | public IItemStack finalOutput;
23 | public ArrayList items;
24 | public boolean hasTrackingInfo = false;
25 | public long timeStarted = 0L;
26 | public long timeElapsed = 0L;
27 | }
28 |
29 | String cpuName = null;
30 |
31 | @Override
32 | boolean init(Map getParams) {
33 | if (!getParams.containsKey("cpu")) {
34 | noParam("cpu");
35 | return false;
36 | }
37 | cpuName = getParams.get("cpu");
38 | return true;
39 | }
40 |
41 | @Override
42 | void handle(IAEGrid grid) {
43 | if (grid == null) {
44 | deny("GRID_NOT_FOUND");
45 | return;
46 | }
47 | IAECraftingGrid craftingGrid = grid.web$getCraftingGrid();
48 |
49 | ICraftingCPUCluster cpu = GetCPUList.getCPUList(craftingGrid)
50 | .get(cpuName);
51 | if (cpu == null) {
52 | deny("CPU_NOT_FOUND");
53 | return;
54 | }
55 |
56 | JSON_ClusterData clusterData = new JSON_ClusterData();
57 | clusterData.size = cpu.web$getAvailableStorage();
58 | clusterData.isBusy = cpu.web$isBusy();
59 | if (clusterData.isBusy) {
60 | clusterData.finalOutput = cpu.web$getFinalOutput();
61 | AE2JobTracker.JobTrackingInfo trackingInfo = AE2JobTracker.trackingInfoMap.get(cpu);
62 | clusterData.hasTrackingInfo = trackingInfo != null;
63 |
64 | HashMap prep = new HashMap<>();
65 | IItemList items = AE2Controller.AE2Interface.web$createItemList();
66 | cpu.web$getActiveItems(items);
67 | for (IItemStack itemStack : items) {
68 | JSON_CompactedItem compactedItem = JSON_CompactedItem.create(itemStack);
69 | prep.computeIfAbsent(compactedItem, k -> compactedItem).active += itemStack.web$getStackSize();
70 | }
71 | items = AE2Controller.AE2Interface.web$createItemList();
72 | cpu.web$getPendingItems(items);
73 | for (IItemStack itemStack : items) {
74 | JSON_CompactedItem compactedItem = JSON_CompactedItem.create(itemStack);
75 | prep.computeIfAbsent(compactedItem, k -> compactedItem).pending += itemStack.web$getStackSize();
76 | }
77 | items = AE2Controller.AE2Interface.web$createItemList();
78 | cpu.web$getStorageItems(items);
79 | for (IItemStack itemStack : items) {
80 | JSON_CompactedItem compactedItem = JSON_CompactedItem.create(itemStack);
81 | prep.computeIfAbsent(compactedItem, k -> compactedItem).stored += itemStack.web$getStackSize();
82 | }
83 |
84 | if (clusterData.hasTrackingInfo) {
85 | clusterData.timeStarted = trackingInfo.timeStarted;
86 | clusterData.timeElapsed = (System.currentTimeMillis()) - clusterData.timeStarted;
87 | for (IItemStack stack : trackingInfo.timeSpentOn.keySet()) {
88 | JSON_CompactedItem compactedItem = JSON_CompactedItem.create(stack);
89 | JSON_CompactedItem finalCompactedItem = compactedItem;
90 | compactedItem = prep.computeIfAbsent(compactedItem, k -> finalCompactedItem);
91 | compactedItem.timeSpentCrafting += trackingInfo.getTimeSpentOn(stack);
92 | compactedItem.craftedTotal += trackingInfo.craftedTotal.getOrDefault(stack, 0L);
93 | compactedItem.shareInCraftingTime += trackingInfo.getShareInCraftingTime(stack);
94 | compactedItem.shareInCraftingTimeCombined = Math
95 | .min(((double) compactedItem.timeSpentCrafting) / (double) clusterData.timeElapsed, 1d);
96 | compactedItem.craftsPerSec = (double) compactedItem.craftedTotal
97 | / (compactedItem.timeSpentCrafting / 1000d);
98 | }
99 | }
100 |
101 | clusterData.items = new ArrayList<>(prep.values());
102 | // TODO Move sorting to javascript!
103 | clusterData.items.sort((i1, i2) -> {
104 | if (i1.active > 0 && i2.active > 0) return Long.compare(i2.active, i1.active);
105 | else if (i1.active > 0 && i2.active == 0) return -1;
106 | else if (i1.active == 0 && i2.active > 0) return 1;
107 | if (i1.pending > 0 && i2.pending > 0) return Long.compare(i2.pending, i1.pending);
108 | else if (i1.pending > 0 && i2.pending == 0) return -1;
109 | else if (i1.pending == 0 && i2.pending > 0) return 1;
110 | return Long.compare(i2.stored, i1.stored);
111 | });
112 |
113 | }
114 |
115 | setData(clusterData);
116 | done();
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPUList.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import java.util.LinkedHashMap;
4 | import java.util.Map;
5 |
6 | import pl.kuba6000.ae2webintegration.core.AE2JobTracker;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
11 |
12 | public class GetCPUList extends ISyncedRequest {
13 |
14 | private static class JSON_CpuInfo {
15 |
16 | public boolean isBusy;
17 | public IItemStack finalOutput;
18 | public long availableStorage;
19 | public long usedStorage;
20 | public long coProcessors;
21 | public boolean hasTrackingInfo = false;
22 | public long timeStarted = 0L;
23 | }
24 |
25 | public static Map getCPUList(IAECraftingGrid craftingGrid) {
26 | LinkedHashMap orderedMap = new LinkedHashMap<>();
27 | for (ICraftingCPUCluster cpu : craftingGrid.web$getCPUs()) {
28 | String name = cpu.web$getName();
29 | orderedMap.put(name, cpu);
30 | }
31 | return orderedMap;
32 | }
33 |
34 | @Override
35 | boolean init(Map getParams) {
36 | return true;
37 | }
38 |
39 | @Override
40 | void handle(IAEGrid grid) {
41 | if (grid == null) {
42 | deny("GRID_NOT_FOUND");
43 | return;
44 | }
45 | Map clusters = getCPUList(grid.web$getCraftingGrid());
46 | LinkedHashMap cpuList = new LinkedHashMap<>(clusters.size());
47 | for (Map.Entry entry : clusters.entrySet()) {
48 | JSON_CpuInfo cpuInfo = new JSON_CpuInfo();
49 | ICraftingCPUCluster cluster = entry.getValue();
50 | cpuInfo.availableStorage = cluster.web$getAvailableStorage();
51 | cpuInfo.usedStorage = cluster.web$getUsedStorage();
52 | cpuInfo.coProcessors = cluster.web$getCoProcessors();
53 | if (cpuInfo.isBusy = cluster.web$isBusy()) {
54 | cpuInfo.finalOutput = cluster.web$getFinalOutput();
55 | AE2JobTracker.JobTrackingInfo trackingInfo = AE2JobTracker.trackingInfoMap.get(cluster);
56 | if (cpuInfo.hasTrackingInfo = trackingInfo != null) {
57 | cpuInfo.timeStarted = trackingInfo.timeStarted;
58 | }
59 | }
60 | cpuList.put(entry.getKey(), cpuInfo);
61 | }
62 | setData(cpuList);
63 | done();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetGridList.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import java.util.ArrayList;
4 |
5 | import com.mojang.authlib.GameProfile;
6 |
7 | import pl.kuba6000.ae2webintegration.core.GridData;
8 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.IAE;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid;
13 |
14 | public class GetGridList extends ISyncedRequest {
15 |
16 | private static class JSON_GridData {
17 |
18 | JSON_GridData(long key, int cpuCount, String owner, boolean isOwned, boolean isTrackingEnabled) {
19 | this.key = key;
20 | this.cpuCount = cpuCount;
21 | this.owner = owner;
22 | this.isOwned = isOwned;
23 | this.isTrackingEnabled = isTrackingEnabled;
24 | }
25 |
26 | public long key; // key == -1 -> not attachable
27 | public int cpuCount;
28 | public String owner;
29 | public boolean isOwned;
30 | public boolean isTrackingEnabled = false;
31 | }
32 |
33 | @Override
34 | public void handle(IAE ae) {
35 | ArrayList grids = new ArrayList<>();
36 | for (IAEGrid grid : ae.web$getGrids()) {
37 | IAEPathingGrid pathing = grid.web$getPathingGrid();
38 | if (pathing == null || pathing.web$isNetworkBooting()
39 | || pathing.web$getControllerState() != AEControllerState.CONTROLLER_ONLINE) {
40 | continue;
41 | }
42 | IAESecurityGrid security = grid.web$getSecurityGrid();
43 | if (security == null || !security.web$isAvailable() || security.web$getSecurityKey() == -1) {
44 | if (context.isAdmin()) {
45 | grids.add(
46 | new JSON_GridData(
47 | -1,
48 | grid.web$getCraftingGrid()
49 | .web$getCPUCount(),
50 | "N/A",
51 | false,
52 | false));
53 | }
54 | continue;
55 | }
56 | if (!context.isAdmin() && !security.web$hasPermissions(context.getUserID())) {
57 | continue;
58 | }
59 | GameProfile gameProfile = security.web$getOwnerProfile();
60 | GridData gridData = GridData.get(security.web$getSecurityKey());
61 | grids.add(
62 | new JSON_GridData(
63 | security.web$getSecurityKey(),
64 | grid.web$getCraftingGrid()
65 | .web$getCPUCount(),
66 | gameProfile == null ? "N/A" : gameProfile.getName(),
67 | security.web$hasPermissions(context.getUserID()),
68 | gridData.isTracked));
69 | }
70 | grids.sort((d1, d2) -> {
71 | if (d1.isOwned && !d2.isOwned) {
72 | return -1;
73 | } else if (!d1.isOwned && d2.isOwned) {
74 | return 1;
75 | } else if (d1.isTrackingEnabled && !d2.isTrackingEnabled) {
76 | return -1;
77 | } else if (!d1.isTrackingEnabled && d2.isTrackingEnabled) {
78 | return 1;
79 | } else if (d1.key == -1 && d2.key != -1) {
80 | return 1; // unattached grids go to the end
81 | } else if (d1.key != -1 && d2.key == -1) {
82 | return -1; // attached grids come first
83 | } else {
84 | return Integer.compare(d2.cpuCount, d1.cpuCount); // sort by cpu count if all else is equal
85 | }
86 | });
87 | setData(grids);
88 | done();
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetItems.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Map;
5 |
6 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
7 | import pl.kuba6000.ae2webintegration.core.api.JSON_DetailedItem;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid;
12 |
13 | public class GetItems extends ISyncedRequest {
14 |
15 | @Override
16 | boolean init(Map getParams) {
17 | return true;
18 | }
19 |
20 | @Override
21 | void handle(IAEGrid grid) {
22 | if (grid == null) {
23 | deny("GRID_NOT_FOUND");
24 | return;
25 | }
26 | IAEStorageGrid storageGrid = grid.web$getStorageGrid();
27 | IItemList storageList = storageGrid.web$getItemStorageList();
28 | AE2Controller.hashcodeToAEItemStack.clear();
29 | ArrayList items = new ArrayList<>();
30 | for (IItemStack stack : storageList) {
31 | int hash;
32 | AE2Controller.hashcodeToAEItemStack.put(hash = stack.hashCode(), stack);
33 | JSON_DetailedItem detailedItem = new JSON_DetailedItem();
34 | detailedItem.itemid = stack.web$getItemID();
35 | detailedItem.itemname = stack.web$getDisplayName();
36 | detailedItem.quantity = stack.web$getStackSize();
37 | detailedItem.craftable = stack.web$isCraftable();
38 | detailedItem.hashcode = hash;
39 | items.add(detailedItem);
40 | }
41 | setData(items);
42 | done();
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/ISyncedRequest.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import java.util.Map;
4 |
5 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
6 | import pl.kuba6000.ae2webintegration.core.GridData;
7 | import pl.kuba6000.ae2webintegration.core.ae2request.IRequest;
8 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.IAE;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid;
13 |
14 | public abstract class ISyncedRequest extends IRequest {
15 |
16 | protected AE2Controller.RequestContext context = null;
17 | protected long gridKey = -1;
18 | protected IAEGrid grid = null;
19 | protected GridData gridData = null;
20 |
21 | boolean init(Map getParams) {
22 | return true;
23 | }
24 |
25 | public boolean init(AE2Controller.RequestContext context) {
26 | this.context = context;
27 | String gridstr = context.getGetParams()
28 | .get("grid");
29 | if (gridstr == null || gridstr.isEmpty()) gridKey = -1;
30 | else gridKey = Long.parseLong(gridstr);
31 | return init(context.getGetParams());
32 | }
33 |
34 | void handle(IAEGrid grid) {}
35 |
36 | public void handle(IAE ae) {
37 | if (gridKey != -1) {
38 | for (IAEGrid grid : ae.web$getGrids()) {
39 | IAEPathingGrid pathing = grid.web$getPathingGrid();
40 | if (pathing == null || pathing.web$isNetworkBooting()
41 | || pathing.web$getControllerState() != AEControllerState.CONTROLLER_ONLINE) {
42 | continue;
43 | }
44 | IAESecurityGrid security = grid.web$getSecurityGrid();
45 | if (security == null || !security.web$isAvailable()) {
46 | continue;
47 | }
48 | if (gridKey == security.web$getSecurityKey()) {
49 | if (!context.isAdmin() && !security.web$hasPermissions(context.getUserID())) {
50 | deny("NO_PERMISSIONS");
51 | return;
52 | }
53 | this.grid = grid;
54 | }
55 | }
56 | }
57 | if (grid != null) gridData = GridData.get(gridKey);
58 | handle(grid);
59 | }
60 |
61 | @Override
62 | public void handle(AE2Controller.RequestContext context) {
63 | throw new IllegalArgumentException("ONLY SYNCED");
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Job.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Map;
5 | import java.util.concurrent.ExecutionException;
6 | import java.util.concurrent.Future;
7 |
8 | import net.minecraft.util.IChatComponent;
9 |
10 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
11 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEActionable;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob;
13 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
14 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEMeInventoryItem;
15 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
16 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
17 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
18 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
19 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid;
20 |
21 | public class Job extends ISyncedRequest {
22 |
23 | private static class JSON_JobData {
24 |
25 | boolean isDone;
26 | public boolean isSimulating;
27 | public long bytesTotal;
28 | public ArrayList plan;
29 |
30 | public static class JobItem {
31 |
32 | public String itemid;
33 | public String itemname;
34 | public long stored;
35 | public long requested;
36 | public long missing;
37 | public long steps;
38 | public double usedPercent;
39 | }
40 | }
41 |
42 | private enum ERequestType {
43 | CHECK,
44 | CANCEL,
45 | SUBMIT
46 | }
47 |
48 | private ERequestType type = null;
49 | private int jobID;
50 | private String cpuName;
51 |
52 | @Override
53 | boolean init(Map getParams) {
54 | if (!getParams.containsKey("id")) {
55 | noParam("id");
56 | return false;
57 | }
58 | this.jobID = Integer.parseInt(getParams.get("id"));
59 | if (getParams.containsKey("cancel")) this.type = ERequestType.CANCEL;
60 | else if (getParams.containsKey("submit")) {
61 | this.type = ERequestType.SUBMIT;
62 | if (getParams.containsKey("cpu")) this.cpuName = getParams.get("cpu");
63 | } else this.type = ERequestType.CHECK;
64 | return true;
65 | }
66 |
67 | @Override
68 | void handle(IAEGrid grid) {
69 | if (grid == null) {
70 | deny("GRID_NOT_FOUND");
71 | return;
72 | }
73 | Future job = gridData.jobs.get(jobID);
74 | if (job == null) {
75 | deny("INVALID_ID");
76 | return;
77 | }
78 | if (type == ERequestType.CHECK) {
79 | JSON_JobData jobData = new JSON_JobData();
80 | if (jobData.isDone = job.isDone()) {
81 | try {
82 | IAECraftingJob craftingJob = job.get();
83 | IAEStorageGrid storageGrid = grid.web$getStorageGrid();
84 | IAEMeInventoryItem items = storageGrid.web$getItemInventory();
85 | jobData.isSimulating = craftingJob.web$isSimulation();
86 | jobData.bytesTotal = craftingJob.web$getByteTotal();
87 | IItemList plan;
88 | craftingJob.web$populatePlan(plan = AE2Controller.AE2Interface.web$createItemList());
89 | jobData.plan = new ArrayList<>();
90 | for (IItemStack stack : plan) {
91 | JSON_JobData.JobItem jobItem = new JSON_JobData.JobItem();
92 | jobItem.itemid = stack.web$getItemID();
93 | jobItem.itemname = stack.web$getDisplayName();
94 | jobItem.requested = stack.web$getCountRequestable();
95 | jobItem.steps = stack.web$getCountRequestableCrafts();
96 | if (jobData.isSimulating) {
97 | IItemStack toExtract = stack.web$copy();
98 | toExtract.web$reset();
99 | toExtract.web$setStackSize(stack.web$getStackSize());
100 | IItemStack missing = toExtract.web$copy();
101 | toExtract = items.web$extractItems(toExtract, AEActionable.SIMULATE, grid);
102 | if (toExtract == null) {
103 | toExtract = missing.web$copy();
104 | toExtract.web$setStackSize(0);
105 | }
106 | jobItem.stored = toExtract.web$getStackSize();
107 | jobItem.missing = missing.web$getStackSize() - toExtract.web$getStackSize();
108 | } else {
109 | jobItem.stored = stack.web$getStackSize();
110 | jobItem.missing = 0;
111 | }
112 | if (jobItem.missing == 0 && jobItem.requested == 0 && jobItem.stored > 0) {
113 | IItemStack realStack = items.web$getAvailableItem(stack);
114 | long available = 0L;
115 | if (realStack != null) available = realStack.web$getStackSize();
116 | if (available > 0L) jobItem.usedPercent = (double) jobItem.stored / (double) available;
117 | }
118 | jobData.plan.add(jobItem);
119 | }
120 | // TODO Move sorting to javascript!
121 | jobData.plan.sort((i1, i2) -> {
122 | if (i1.missing > 0 && i2.missing > 0) return Long.compare(i2.missing, i1.missing);
123 | else if (i1.missing > 0 && i2.missing == 0) return -1;
124 | else if (i1.missing == 0 && i2.missing > 0) return 1;
125 | if (i1.requested > 0 && i2.requested > 0) return Long.compare(i2.steps, i1.steps);
126 | else if (i1.requested > 0 && i2.requested == 0) return -1;
127 | else if (i1.requested == 0 && i2.requested > 0) return 1;
128 | return Long.compare(i2.stored, i1.stored);
129 | });
130 | } catch (InterruptedException | ExecutionException e) {
131 | e.printStackTrace();
132 | deny("INTERNAL_ERROR");
133 | return;
134 | }
135 | }
136 | setData(jobData);
137 | done();
138 | } else if (type == ERequestType.CANCEL) {
139 | job.cancel(true);
140 | gridData.jobs.remove(this.jobID);
141 | done();
142 | } else if (type == ERequestType.SUBMIT) {
143 | IAECraftingGrid craftingGrid = grid.web$getCraftingGrid();
144 | if (job.isDone()) {
145 | try {
146 | IAECraftingJob craftingJob = job.get();
147 | ICraftingCPUCluster target = null;
148 | if (cpuName != null) {
149 | target = GetCPUList.getCPUList(craftingGrid)
150 | .get(cpuName);
151 | if (target == null) {
152 | deny("CPU_NOT_FOUND");
153 | return;
154 | }
155 | }
156 | IChatComponent error = craftingGrid.web$submitJob(craftingJob, target, true, grid);
157 | if (error != null) {
158 | deny("FAIL");
159 | setData(error.getUnformattedTextForChat());
160 | } else {
161 | done();
162 | }
163 | } catch (InterruptedException | ExecutionException e) {
164 | e.printStackTrace();
165 | deny("INTERNAL_ERROR");
166 | }
167 | } else {
168 | deny("JOB_NOT_DONE");
169 | }
170 | }
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Order.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.ae2request.sync;
2 |
3 | import static pl.kuba6000.ae2webintegration.core.AE2Controller.hashcodeToAEItemStack;
4 |
5 | import java.util.Map;
6 | import java.util.concurrent.Future;
7 |
8 | import com.google.gson.JsonObject;
9 |
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
12 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
13 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
14 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
15 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
16 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid;
17 |
18 | public class Order extends ISyncedRequest {
19 |
20 | private IItemStack item;
21 |
22 | @Override
23 | boolean init(Map getParams) {
24 | if (!getParams.containsKey("item") || !getParams.containsKey("quantity")) {
25 | noParam("item", "quantity");
26 | return false;
27 | }
28 | int hash = Integer.parseInt(getParams.get("item"));
29 | int quantity = Integer.parseInt(getParams.get("quantity"));
30 | this.item = hashcodeToAEItemStack.get(hash);
31 | if (this.item == null || !this.item.web$isCraftable()) {
32 | deny("ITEM_NOT_FOUND");
33 | return false;
34 | }
35 | this.item = this.item.web$copy();
36 | this.item.web$setStackSize(quantity);
37 | return true;
38 | }
39 |
40 | @Override
41 | void handle(IAEGrid grid) {
42 | if (grid == null) {
43 | deny("GRID_NOT_FOUND");
44 | return;
45 | }
46 | IAECraftingGrid craftingGrid = grid.web$getCraftingGrid();
47 | boolean allBusy = true;
48 | for (ICraftingCPUCluster cpu : craftingGrid.web$getCPUs()) {
49 | if (!cpu.web$isBusy()) {
50 | allBusy = false;
51 | break;
52 | }
53 | }
54 | if (!allBusy) {
55 | IAEStorageGrid storageGrid = grid.web$getStorageGrid();
56 | final IItemList itemList = storageGrid.web$getItemStorageList();
57 | IItemStack realItem = itemList.web$findPrecise(this.item);
58 | if (realItem != null && realItem.web$isCraftable()) {
59 | Future job = craftingGrid.web$beginCraftingJob(grid, this.item);
60 |
61 | int jobID = gridData.addJob(job);
62 | JsonObject jobData = new JsonObject();
63 | jobData.addProperty("jobID", jobID);
64 | if (gridData.jobs.size() > 3) {
65 | int toDeleteBelowAndEqual = jobID - 3;
66 | gridData.jobs.entrySet()
67 | .removeIf(integerFutureEntry -> integerFutureEntry.getKey() <= toDeleteBelowAndEqual);
68 | }
69 | setData(jobData);
70 | done();
71 | } else {
72 | deny("ITEM_NOT_FOUND");
73 | }
74 | } else {
75 | deny("ALL_CPU_BUSY");
76 | }
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEActionable.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api.AEApi;
2 |
3 | public enum AEActionable {
4 | MODULATE,
5 | SIMULATE
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEControllerState.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api.AEApi;
2 |
3 | public enum AEControllerState {
4 | NO_CONTROLLER,
5 | CONTROLLER_ONLINE,
6 | CONTROLLER_CONFLICT,
7 | // not implemented
8 | UNSUPPORTED
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/DimensionalCoords.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api;
2 |
3 | import java.util.Objects;
4 |
5 | import net.minecraft.world.World;
6 |
7 | public class DimensionalCoords {
8 |
9 | int dimid;
10 | int x;
11 | int y;
12 | int z;
13 |
14 | public DimensionalCoords(int dimid, int x, int y, int z) {
15 | this.dimid = dimid;
16 | this.x = x;
17 | this.y = y;
18 | this.z = z;
19 | }
20 |
21 | public DimensionalCoords(World world, int x, int y, int z) {
22 | this.dimid = world.provider.dimensionId;
23 | this.x = x;
24 | this.y = y;
25 | this.z = z;
26 | }
27 |
28 | @Override
29 | public int hashCode() {
30 | return Objects.hash(dimid, x, y, z);
31 | }
32 |
33 | @Override
34 | public boolean equals(Object obj) {
35 | return obj instanceof DimensionalCoords coords && coords.dimid == dimid
36 | && coords.x == x
37 | && coords.y == y
38 | && coords.z == z;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEMixinCallbacks.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api;
2 |
3 | import pl.kuba6000.ae2webintegration.core.AEMixinCallbacks;
4 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails;
5 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
10 |
11 | public interface IAEMixinCallbacks {
12 |
13 | static IAEMixinCallbacks getInstance() {
14 | return AEMixinCallbacks.INSTANCE;
15 | }
16 |
17 | void jobStarted(ICraftingCPUCluster cpuCluster, IAECraftingGrid cache, IAEGrid grid, boolean isMerging,
18 | boolean isAuthorPlayer);
19 |
20 | void craftingStatusPostedUpdate(ICraftingCPUCluster cpu, IItemStack diff);
21 |
22 | void pushedPattern(ICraftingCPUCluster cpu, IPatternProviderViewable provider, IAECraftingPatternDetails details);
23 |
24 | void jobCompleted(IAEGrid grid, ICraftingCPUCluster cpu);
25 |
26 | void jobCancelled(IAEGrid grid, ICraftingCPUCluster cpu);
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEWebInterface.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api;
2 |
3 | import com.mojang.authlib.GameProfile;
4 |
5 | import pl.kuba6000.ae2webintegration.core.AEWebAPI;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.IAE;
7 |
8 | public interface IAEWebInterface {
9 |
10 | static IAEWebInterface getInstance() {
11 | return AEWebAPI.INSTANCE;
12 | }
13 |
14 | GameProfile getAEWebGameProfile();
15 |
16 | void initAEInterface(IAE ae);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedItem.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api;
2 |
3 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
4 | import pl.kuba6000.ae2webintegration.core.utils.GSONUtils;
5 |
6 | public class JSON_CompactedItem {
7 |
8 | @GSONUtils.SkipGSON
9 | private final IItemStack internalItem;
10 | @GSONUtils.SkipGSON
11 | private final int hashcode;
12 |
13 | public final String itemid;
14 | public final String itemname;
15 | public long active = 0;
16 | public long pending = 0;
17 | public long stored = 0;
18 | public long timeSpentCrafting = 0;
19 | public long craftedTotal = 0;
20 | public double shareInCraftingTime = 0d;
21 | public double shareInCraftingTimeCombined = 0d;
22 | public double craftsPerSec = 0d;
23 |
24 | public JSON_CompactedItem(IItemStack itemStack) {
25 | this.internalItem = itemStack;
26 | this.hashcode = this.internalItem.hashCode();
27 | this.itemid = itemStack.web$getItemID();
28 | this.itemname = itemStack.web$getDisplayName();
29 | }
30 |
31 | public static JSON_CompactedItem create(IItemStack stack) {
32 | return new JSON_CompactedItem(stack);
33 | }
34 |
35 | @Override
36 | public int hashCode() {
37 | return hashcode;
38 | }
39 |
40 | @Override
41 | public boolean equals(Object obj) {
42 | if (obj instanceof JSON_CompactedItem) {
43 | return ((JSON_CompactedItem) obj).internalItem.equals(this.internalItem);
44 | }
45 | return false;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedJobTrackingInfo.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashSet;
5 | import java.util.Map;
6 |
7 | import org.apache.commons.lang3.tuple.Pair;
8 |
9 | import pl.kuba6000.ae2webintegration.core.AE2JobTracker;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
11 |
12 | public class JSON_CompactedJobTrackingInfo {
13 |
14 | public static class timingClass {
15 |
16 | long started;
17 | long ended;
18 |
19 | public timingClass(long started, long ended) {
20 | this.started = started;
21 | this.ended = ended;
22 | }
23 | }
24 |
25 | public static class CompactedTrackingGSONItem {
26 |
27 | public String itemid;
28 | public String itemname;
29 | public long timeSpentOn;
30 | public long craftedTotal;
31 | public double shareInCraftingTime = 0d;
32 | public double shareInCraftingTimeCombined = 0d;
33 | public double craftsPerSec = 0d;
34 |
35 | public ArrayList timings = new ArrayList<>();
36 | }
37 |
38 | public IItemStack finalOutput;
39 | public long timeStarted;
40 | public long timeDone;
41 | public boolean wasCancelled;
42 | public ArrayList items = new ArrayList<>();
43 |
44 | public static class AEInterfaceGSON {
45 |
46 | String name;
47 |
48 | public ArrayList timings = new ArrayList<>();
49 | public long timingsCombined;
50 |
51 | public HashSet location = new HashSet<>();
52 | }
53 |
54 | public ArrayList interfaceShare = new ArrayList<>();
55 |
56 | public JSON_CompactedJobTrackingInfo(AE2JobTracker.JobTrackingInfo info) {
57 | this.finalOutput = info.finalOutput;
58 | this.timeStarted = info.timeStarted;
59 | this.timeDone = info.timeDone;
60 | long elapsed = this.timeDone - this.timeStarted;
61 | this.wasCancelled = info.wasCancelled;
62 | for (Map.Entry entry : info.timeSpentOn.entrySet()) {
63 | IItemStack stack = entry.getKey();
64 | long spent = entry.getValue();
65 | CompactedTrackingGSONItem item = new CompactedTrackingGSONItem();
66 | item.itemid = stack.web$getItemID();
67 | item.itemname = stack.web$getDisplayName();
68 | item.timeSpentOn = spent;
69 | item.craftedTotal = info.craftedTotal.get(stack);
70 | item.shareInCraftingTime = info.getShareInCraftingTime(stack);
71 | item.shareInCraftingTimeCombined = Math.min(((double) item.timeSpentOn) / (double) elapsed, 1d);
72 | item.craftsPerSec = (double) item.craftedTotal / (item.timeSpentOn / 1000d);
73 | for (Pair longLongPair : info.itemShare.get(stack)) {
74 | item.timings.add(new timingClass(longLongPair.getKey(), longLongPair.getValue()));
75 | }
76 | items.add(item);
77 | }
78 | items.sort((i1, i2) -> Double.compare(i2.shareInCraftingTime, i1.shareInCraftingTime));
79 | for (Map.Entry>> entry : info.interfaceShare.entrySet()) {
80 | AEInterfaceGSON interfaceGSON = new AEInterfaceGSON();
81 | interfaceGSON.name = entry.getKey().name;
82 | interfaceGSON.location = entry.getKey().location;
83 | for (Pair longLongPair : entry.getValue()) {
84 | interfaceGSON.timings.add(new timingClass(longLongPair.getKey(), longLongPair.getValue()));
85 | }
86 | long interfaceElapsed = 0L;
87 | for (Pair pair : entry.getValue()) {
88 | interfaceElapsed += pair.getValue() - pair.getKey();
89 | }
90 | interfaceGSON.timingsCombined = interfaceElapsed;
91 | interfaceShare.add(interfaceGSON);
92 | }
93 | interfaceShare.sort((i1, i2) -> Long.compare(i2.timingsCombined, i1.timingsCombined));
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_DetailedItem.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.api;
2 |
3 | public class JSON_DetailedItem {
4 |
5 | public int hashcode;
6 | public String itemid;
7 | public String itemname;
8 | public long quantity;
9 | public boolean craftable;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/commands/BaseCommandHandler.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.commands;
2 |
3 | import java.util.UUID;
4 |
5 | import net.minecraft.command.CommandBase;
6 | import net.minecraft.command.ICommandSender;
7 | import net.minecraft.entity.player.EntityPlayerMP;
8 | import net.minecraft.util.ChatComponentText;
9 | import net.minecraft.util.ChatComponentTranslation;
10 | import net.minecraft.util.EnumChatFormatting;
11 |
12 | import org.apache.commons.lang3.tuple.Pair;
13 |
14 | import pl.kuba6000.ae2webintegration.core.AE2Controller;
15 | import pl.kuba6000.ae2webintegration.core.Config;
16 | import pl.kuba6000.ae2webintegration.core.WebData;
17 |
18 | public class BaseCommandHandler extends CommandBase {
19 |
20 | @Override
21 | public String getCommandName() {
22 | return "ae2webintegration";
23 | }
24 |
25 | @Override
26 | public String getCommandUsage(ICommandSender sender) {
27 | return "ae2webintegration ";
28 | }
29 |
30 | @Override
31 | public int getRequiredPermissionLevel() {
32 | return 0;
33 | }
34 |
35 | @Override
36 | public void processCommand(ICommandSender sender, String[] args) {
37 | if (sender.getEntityWorld().isRemote) return;
38 | if (args.length == 0 || (!args[0].equals("reload") && !args[0].equals("auth"))) {
39 | sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "/ae2webintegration "));
40 | return;
41 | }
42 | if (args[0].equals("reload")) {
43 | if (!sender.canCommandSenderUseCommand(4, getCommandName())) {
44 | ChatComponentTranslation chatcomponenttranslation2 = new ChatComponentTranslation(
45 | "commands.generic.permission",
46 | new Object[0]);
47 | chatcomponenttranslation2.getChatStyle()
48 | .setColor(EnumChatFormatting.RED);
49 | sender.addChatMessage(chatcomponenttranslation2);
50 | return;
51 | }
52 | Config.synchronizeConfiguration();
53 | AE2Controller.stopHTTPServer();
54 | AE2Controller.startHTTPServer();
55 | sender.addChatMessage(
56 | new ChatComponentText(
57 | EnumChatFormatting.GREEN + "Successfully reloaded the config and restarted the web server!"));
58 | } else {
59 | // auth command
60 | if (args.length < 2) {
61 | sender
62 | .addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "/ae2webintegration auth "));
63 | return;
64 | }
65 |
66 | String token = args[1];
67 |
68 | if (!(sender instanceof EntityPlayerMP)) {
69 | sender.addChatMessage(
70 | new ChatComponentText(EnumChatFormatting.RED + "This command can only be used by players!"));
71 | return;
72 | }
73 |
74 | UUID id = ((EntityPlayerMP) sender).getUniqueID();
75 |
76 | Pair p = AE2Controller.awaitingRegistration.get(id);
77 | if (p == null) {
78 | sender.addChatMessage(
79 | new ChatComponentText(
80 | EnumChatFormatting.RED
81 | + "You have to initialize the registration on the web interface first!"));
82 | return;
83 | }
84 |
85 | if (!p.getLeft()
86 | .equals(token)) {
87 | sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "Invalid token!"));
88 | return;
89 | }
90 |
91 | WebData.setPassword(((EntityPlayerMP) sender).getGameProfile(), p.getRight());
92 |
93 | AE2Controller.awaitingRegistration.remove(id);
94 |
95 | sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GREEN + "Registered successfully!"));
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/discord/DiscordManager.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.discord;
2 |
3 | import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.MODID;
4 |
5 | import java.io.IOException;
6 | import java.io.OutputStream;
7 | import java.net.URL;
8 | import java.util.concurrent.ConcurrentLinkedQueue;
9 |
10 | import javax.net.ssl.HttpsURLConnection;
11 |
12 | import org.apache.logging.log4j.LogManager;
13 | import org.apache.logging.log4j.Logger;
14 |
15 | import com.google.gson.JsonArray;
16 | import com.google.gson.JsonObject;
17 |
18 | import pl.kuba6000.ae2webintegration.core.Config;
19 |
20 | public class DiscordManager extends Thread {
21 |
22 | private static final Logger LOG = LogManager.getLogger(MODID + " - DISCORD INTEGRATION");
23 |
24 | private static DiscordManager thread;
25 |
26 | private static ConcurrentLinkedQueue toPush = new ConcurrentLinkedQueue<>();
27 |
28 | public static void init() {
29 | if (thread != null) return;
30 | thread = new DiscordManager();
31 | thread.start();
32 | }
33 |
34 | public static void postMessageNonBlocking(DiscordEmbed message) {
35 | toPush.offer(message);
36 | }
37 |
38 | public static class DiscordEmbed {
39 |
40 | String title;
41 | String description;
42 | int color;
43 |
44 | public DiscordEmbed(String title, String description, int color) {
45 | this.title = title;
46 | this.description = description;
47 | this.color = color;
48 | }
49 |
50 | public DiscordEmbed(String title, String description) {
51 | this(title, description, 1752220);
52 | }
53 | }
54 |
55 | private static void postMessage(DiscordEmbed message) {
56 | if (Config.DISCORD_WEBHOOK.isEmpty()) return;
57 |
58 | String roleID = Config.DISCORD_ROLE_ID;
59 |
60 | JsonObject json = new JsonObject();
61 | json.addProperty("username", "AE2 Web Integration");
62 | json.addProperty("content", !roleID.isEmpty() ? "<@&" + roleID + ">" : "");
63 | JsonArray embeds = new JsonArray();
64 | JsonObject embed = new JsonObject();
65 | embed.addProperty("title", message.title);
66 | embed.addProperty("description", message.description);
67 | embed.addProperty("color", message.color);
68 | embeds.add(embed);
69 | json.add("embeds", embeds);
70 | json.add("attachments", new JsonArray());
71 |
72 | URL url = null;
73 | try {
74 | url = new URL(Config.DISCORD_WEBHOOK);
75 |
76 | HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
77 | connection.addRequestProperty("Content-Type", "application/json");
78 | connection.addRequestProperty("User-Agent", "AE2-Web-Integration");
79 | connection.setDoOutput(true);
80 | connection.setRequestMethod("POST");
81 |
82 | OutputStream stream = connection.getOutputStream();
83 | stream.write(
84 | json.toString()
85 | .getBytes());
86 | stream.flush();
87 | stream.close();
88 |
89 | int code;
90 | if ((code = connection.getResponseCode()) != 200 && code != 204) {
91 | LOG.error("Error, response code: {}", code);
92 | }
93 | } catch (IOException e) {
94 | // throw new RuntimeException(e);
95 | }
96 | }
97 |
98 | @Override
99 | public void run() {
100 | while (true) {
101 | if (toPush.peek() != null) {
102 | DiscordEmbed message;
103 | while ((message = toPush.poll()) != null) {
104 | postMessage(message);
105 | }
106 | }
107 |
108 | try {
109 | Thread.sleep(1000);
110 | } catch (InterruptedException e) {
111 | // throw new RuntimeException(e);
112 | }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAE.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | public interface IAE {
4 |
5 | Iterable web$getGrids();
6 |
7 | IItemList web$createItemList();
8 |
9 | IAEPlayerData web$getPlayerData();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingJob.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | public interface IAECraftingJob {
4 |
5 | boolean web$isSimulation();
6 |
7 | long web$getByteTotal();
8 |
9 | void web$populatePlan(IItemList plan);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingPatternDetails.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | public interface IAECraftingPatternDetails {
4 |
5 | IItemStack[] web$getCondensedOutputs();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGrid.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | import net.minecraft.util.IChatComponent;
4 |
5 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid;
6 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid;
7 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid;
8 | import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid;
9 |
10 | public interface IAEGrid {
11 |
12 | IAECraftingGrid web$getCraftingGrid();
13 |
14 | IAEPathingGrid web$getPathingGrid();
15 |
16 | IAEStorageGrid web$getStorageGrid();
17 |
18 | IAESecurityGrid web$getSecurityGrid();
19 |
20 | boolean web$isEmpty();
21 |
22 | Object web$getPlayerSource();
23 |
24 | IChatComponent web$getLastFakePlayerChatMessage();
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEMeInventoryItem.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEActionable;
4 |
5 | public interface IAEMeInventoryItem {
6 |
7 | IItemStack web$extractItems(IItemStack stack, AEActionable mode, IAEGrid grid);
8 |
9 | IItemStack web$getAvailableItem(IItemStack stack);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEPlayerData.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | import com.mojang.authlib.GameProfile;
4 |
5 | public interface IAEPlayerData {
6 |
7 | GameProfile web$getPlayerProfile(int playerId);
8 |
9 | int web$getPlayerId(GameProfile id);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingCPUCluster.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | public interface ICraftingCPUCluster {
4 |
5 | void web$setInternalID(int id);
6 |
7 | boolean web$hasCustomName();
8 |
9 | String web$getName();
10 |
11 | long web$getAvailableStorage();
12 |
13 | long web$getUsedStorage();
14 |
15 | long web$getCoProcessors();
16 |
17 | boolean web$isBusy();
18 |
19 | void web$cancel();
20 |
21 | IItemStack web$getFinalOutput();
22 |
23 | void web$getActiveItems(IItemList list);
24 |
25 | void web$getPendingItems(IItemList list);
26 |
27 | void web$getStorageItems(IItemList list);
28 |
29 | IItemList web$getWaitingFor();
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemList.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | public interface IItemList extends Iterable {
4 |
5 | IItemStack web$findPrecise(IItemStack stack);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemStack.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | public interface IItemStack {
4 |
5 | String web$getItemID();
6 |
7 | String web$getDisplayName();
8 |
9 | long web$getStackSize();
10 |
11 | boolean web$isCraftable();
12 |
13 | long web$getCountRequestable();
14 |
15 | long web$getCountRequestableCrafts();
16 |
17 | void web$reset();
18 |
19 | boolean web$isSameType(IItemStack other);
20 |
21 | IItemStack web$copy();
22 |
23 | void web$setStackSize(long size);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IPatternProviderViewable.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces;
2 |
3 | import pl.kuba6000.ae2webintegration.core.api.DimensionalCoords;
4 |
5 | public interface IPatternProviderViewable {
6 |
7 | String web$getName();
8 |
9 | DimensionalCoords web$getLocation();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAECraftingGrid.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces.service;
2 |
3 | import java.util.Set;
4 | import java.util.concurrent.Future;
5 |
6 | import net.minecraft.util.IChatComponent;
7 |
8 | import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob;
9 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid;
10 | import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster;
11 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
12 |
13 | public interface IAECraftingGrid {
14 |
15 | int web$getCPUCount();
16 |
17 | Set web$getCPUs();
18 |
19 | Future web$beginCraftingJob(IAEGrid grid, IItemStack stack);
20 |
21 | IChatComponent web$submitJob(IAECraftingJob job, ICraftingCPUCluster target, boolean prioritizePower, IAEGrid grid);
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEPathingGrid.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces.service;
2 |
3 | import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState;
4 |
5 | public interface IAEPathingGrid {
6 |
7 | boolean web$isNetworkBooting();
8 |
9 | AEControllerState web$getControllerState();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAESecurityGrid.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces.service;
2 |
3 | import com.mojang.authlib.GameProfile;
4 |
5 | public interface IAESecurityGrid {
6 |
7 | boolean web$isAvailable();
8 |
9 | long web$getSecurityKey();
10 |
11 | int web$getOwner();
12 |
13 | GameProfile web$getOwnerProfile();
14 |
15 | boolean web$hasPermissions(int playerId);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEStorageGrid.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.interfaces.service;
2 |
3 | import pl.kuba6000.ae2webintegration.core.interfaces.IAEMeInventoryItem;
4 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemList;
5 |
6 | public interface IAEStorageGrid {
7 |
8 | IItemList web$getItemStorageList();
9 |
10 | IAEMeInventoryItem web$getItemInventory();
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/utils/GSONUtils.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.utils;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import com.google.gson.ExclusionStrategy;
9 | import com.google.gson.FieldAttributes;
10 | import com.google.gson.GsonBuilder;
11 | import com.google.gson.JsonObject;
12 | import com.google.gson.JsonSerializer;
13 |
14 | import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack;
15 |
16 | public class GSONUtils {
17 |
18 | @Retention(RetentionPolicy.RUNTIME)
19 | @Target(ElementType.FIELD)
20 | public @interface SkipGSON {}
21 |
22 | private static final ExclusionStrategy GSONStrategy = new ExclusionStrategy() {
23 |
24 | @Override
25 | public boolean shouldSkipField(FieldAttributes f) {
26 | return f.getAnnotation(SkipGSON.class) != null;
27 | }
28 |
29 | @Override
30 | public boolean shouldSkipClass(Class> clazz) {
31 | return false;
32 | }
33 | };
34 |
35 | private static final JsonSerializer IItemStackSerializer = (src, typeOfSrc, context) -> {
36 | JsonObject json = new JsonObject();
37 | json.addProperty("itemid", src.web$getItemID());
38 | json.addProperty("itemname", src.web$getDisplayName());
39 | json.addProperty("hashcode", src.hashCode());
40 | json.addProperty("quantity", src.web$getStackSize());
41 | return json;
42 | };
43 |
44 | public static final GsonBuilder GSON_BUILDER = new GsonBuilder().addSerializationExclusionStrategy(GSONStrategy)
45 | .addDeserializationExclusionStrategy(GSONStrategy)
46 | .registerTypeHierarchyAdapter(IItemStack.class, IItemStackSerializer)
47 | .serializeNulls();
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/utils/HTTPUtils.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.utils;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.net.URLDecoder;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | public class HTTPUtils {
9 |
10 | public static Map parseQueryString(String qs) {
11 | Map result = new HashMap<>();
12 | if (qs == null) return result;
13 |
14 | int last = 0, next, l = qs.length();
15 | while (last < l) {
16 | next = qs.indexOf('&', last);
17 | if (next == -1) next = l;
18 |
19 | if (next > last) {
20 | int eqPos = qs.indexOf('=', last);
21 | try {
22 | if (eqPos < 0 || eqPos > next) result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), "");
23 | else result.put(
24 | URLDecoder.decode(qs.substring(last, eqPos), "utf-8"),
25 | URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8"));
26 | } catch (UnsupportedEncodingException e) {
27 | throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java
28 | }
29 | }
30 | last = next + 1;
31 | }
32 | return result;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/utils/RateLimiter.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.utils;
2 |
3 | import java.net.InetAddress;
4 | import java.util.HashMap;
5 |
6 | public class RateLimiter {
7 |
8 | private final int MAX_REQUESTS_PER_INTERVAL;
9 | private final int RESET_INTERVAL_MS;
10 | private final int RESET_WHITELIST_INTERVAL_MS; // 1 hour
11 |
12 | public RateLimiter(int maxRequestsPerInterval, int resetIntervalMs, int resetWhitelistIntervalMs) {
13 | MAX_REQUESTS_PER_INTERVAL = maxRequestsPerInterval;
14 | RESET_INTERVAL_MS = resetIntervalMs;
15 | RESET_WHITELIST_INTERVAL_MS = resetWhitelistIntervalMs;
16 | }
17 |
18 | private long lastUpdate = 0;
19 | private final HashMap requestCounter = new HashMap<>();
20 | private final HashMap whitelist = new HashMap<>();
21 |
22 | public boolean isAllowed(InetAddress userId) {
23 | updateRequests();
24 |
25 | if (whitelist.containsKey(userId)) {
26 | return true; // User is whitelisted
27 | }
28 |
29 | return requestCounter.merge(userId, 1, Integer::sum) < MAX_REQUESTS_PER_INTERVAL;
30 | }
31 |
32 | public void ensureWhitelisted(InetAddress userId) {
33 | whitelist.put(userId, System.currentTimeMillis());
34 | }
35 |
36 | private void updateRequests() {
37 | long currentTime = System.currentTimeMillis();
38 |
39 | if (currentTime - lastUpdate > RESET_INTERVAL_MS) { // Reset every 60 seconds
40 | requestCounter.clear();
41 | lastUpdate = currentTime;
42 | }
43 |
44 | whitelist.entrySet()
45 | .removeIf(entry -> currentTime - entry.getValue() > RESET_WHITELIST_INTERVAL_MS); // Remove entries older
46 | // than 1 hour
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/pl/kuba6000/ae2webintegration/core/utils/VersionChecker.java:
--------------------------------------------------------------------------------
1 | package pl.kuba6000.ae2webintegration.core.utils;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.InputStreamReader;
5 | import java.net.HttpURLConnection;
6 | import java.net.URL;
7 |
8 | import com.google.gson.JsonElement;
9 | import com.google.gson.JsonParser;
10 |
11 | import pl.kuba6000.ae2webintegration.Tags;
12 |
13 | public class VersionChecker {
14 |
15 | // example version: 0.0.9-alpha-forge-1.12.2
16 | private static final String VERSION_IDENTIFIER = "-forge-1.7.10";
17 |
18 | private static final String versionCheckURL = "https://api.github.com/repos/kuba6000/AE2-Web-Integration/tags";
19 | private static String latestTag = null;
20 |
21 | private static long lastChecked = 0L;
22 |
23 | private static void updateLatestVersion() {
24 | if (lastChecked != 0L) {
25 | if (!Tags.VERSION.equals(latestTag)) return;
26 | long elapsed = System.currentTimeMillis() - lastChecked;
27 | if (latestTag == null) {
28 | if (elapsed < 5 * 60 * 1000) // 5 minutes
29 | return;
30 | } else if (elapsed < 5 * 60 * 60 * 1000) { // 5 hours
31 | return;
32 | }
33 | }
34 | lastChecked = System.currentTimeMillis();
35 | try {
36 | HttpURLConnection conn = (HttpURLConnection) new URL(versionCheckURL).openConnection();
37 | if (conn.getResponseCode() == 200) {
38 | try (BufferedReader buf = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
39 | JsonElement element = new JsonParser().parse(buf);
40 | // this should be sorted right?
41 | for (JsonElement tag : element.getAsJsonArray()) {
42 | String name = tag.getAsJsonObject()
43 | .get("name")
44 | .getAsString();
45 | if (name.contains(VERSION_IDENTIFIER)) {
46 | latestTag = name;
47 | return;
48 | }
49 | }
50 | // not found???
51 | latestTag = Tags.VERSION;
52 | }
53 | }
54 |
55 | } catch (Exception ignored) {
56 |
57 | }
58 | }
59 |
60 | public static boolean isOutdated() {
61 | updateLatestVersion();
62 | if (latestTag == null) return false;
63 | return !latestTag.equals(Tags.VERSION);
64 | }
65 |
66 | public static String getLatestTag() {
67 | return latestTag;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/resources/LICENSE:
--------------------------------------------------------------------------------
1 | AE2 Web Integration - Minecraft addon
2 | Copyright (C) 2024 kuba6000
3 |
4 | This library is free software; you can redistribute it and/or
5 | modify it under the terms of the GNU Lesser General Public
6 | License as published by the Free Software Foundation; either
7 | version 3 of the License, or (at your option) any later version.
8 |
9 | This library is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | Lesser General Public License for more details.
13 |
14 | You should have received a copy of the GNU Lesser General Public License
15 | along with this library. If not, see .
16 |
--------------------------------------------------------------------------------
/src/main/resources/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kuba6000/AE2-Web-Integration/9aa6e6fe27e58b443b077db3007b48e34e78f9a9/src/main/resources/assets/favicon.ico
--------------------------------------------------------------------------------
/src/main/resources/assets/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
150 |
151 |
152 |
153 | AE2
154 |
155 |
156 | UNIVERSAL WEB TERMINAL
157 |
158 |
159 | Cookie Notice 🍪
160 | Before you continue, please note that this website uses cookies to:
161 |
162 | Save your terminal preferences
163 | Enable login functionality
164 | Improve your overall experience
165 |
166 | By clicking "Accept", you consent to our use of cookies.
167 | Accept Cookies
168 |
169 |
170 |
197 |
198 |
251 |
252 |
253 |
256 |
257 |
258 |
--------------------------------------------------------------------------------
/src/main/resources/mcmod.info:
--------------------------------------------------------------------------------
1 | {
2 | "modListVersion": 2,
3 | "modList": [{
4 | "modid": "ae2webintegration-core",
5 | "name": "AE2WebIntegration-Core",
6 | "description": "AE2 Web Integration mod",
7 | "version": "${modVersion}",
8 | "mcversion": "*",
9 | "url": "https://github.com/kuba6000/AE2-Web-Integration",
10 | "updateUrl": "",
11 | "authorList": ["kuba6000"],
12 | "credits": "",
13 | "logoFile": "",
14 | "screenshots": [],
15 | "parent": "",
16 | "requiredMods": [],
17 | "dependencies": [],
18 | "dependants": [],
19 | "useDependencyInformation": false
20 | },
21 | {
22 | "modid": "ae2webintegration-interface",
23 | "name": "AE2WebIntegration-Interface",
24 | "description": "AE2 Web Integration mod",
25 | "version": "${modVersion}",
26 | "mcversion": "${minecraftVersion}",
27 | "url": "https://github.com/kuba6000/AE2-Web-Integration",
28 | "updateUrl": "",
29 | "authorList": ["kuba6000"],
30 | "credits": "",
31 | "logoFile": "",
32 | "screenshots": [],
33 | "parent": "",
34 | "requiredMods": [],
35 | "dependencies": [],
36 | "dependants": [],
37 | "useDependencyInformation": false
38 | }]
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/resources/mixins.ae2webintegration.json:
--------------------------------------------------------------------------------
1 | {
2 | "required": true,
3 | "minVersion": "0.8.5-GTNH",
4 | "package": "pl.kuba6000.ae2webintegration.ae2interface.mixins",
5 | "plugin": "pl.kuba6000.ae2webintegration.ae2interface.mixins.MixinPlugin",
6 | "refmap": "mixins.ae2webintegration.refmap.json",
7 | "target": "@env(DEFAULT)",
8 | "compatibilityLevel": "JAVA_8",
9 | "mixins": [],
10 | "client": [],
11 | "server": []
12 | }
13 |
--------------------------------------------------------------------------------